JavaRush /Java Blog /Random EN /OOP principles

OOP principles

Published in the Random EN group
Java is an object-oriented language. This means that you need to write Java programs using an object-oriented style. And this style is based on the use of objects and classes in the program.

Basic principles of OOP:

Principles of OOP - 1Let's try, with the help of examples, to understand what classes and objects are, as well as how to apply the basic principles of OOP in practice: abstraction, inheritance, polymorphism and encapsulation.

What is an object?

The world we live in consists of objects. If we look around, we will see that we are surrounded by houses, trees, cars, furniture, dishes, computers. All of these items are objects, and each of them has a set of specific characteristics, behavior and purpose. We are accustomed to objects, and we always use them for very specific purposes. For example, if we need to get to work, we use a car, if we want to eat, we use dishes, and if we need to relax, we need a comfortable sofa. A person is accustomed to thinking objectively to solve problems in everyday life. This was one of the reasons for using objects in programming, and this approach to creating programs was called object-oriented. Let's give an example. Imagine that you have developed a new phone model and want to launch its mass production. As a phone designer, you know what it is for, how it will function, and what parts it will consist of (case, microphone, speaker, wires, buttons, etc.). However, only you know how to connect these parts. However, you do not plan to produce phones personally; for this you have a whole staff of employees. So that you don’t have to explain every time how to connect the parts of the phone, and so that all phones in production turn out the same, before you start producing them, you will need to make a drawing in the form of a description of the phone’s structure. In OOP, such a description, drawing, diagram or template is called a class, from which an object is created when the program is executed. A class is a description of an object that has not yet been created, like a general template consisting of fields, methods and a constructor, and an object is an instance of a class created on the basis of this description.

OOP abstraction

Let's now think about how we can move from an object in the real world to an object in a program, using the phone as an example. The history of this means of communication exceeds 100 years and the modern telephone, unlike its predecessor from the 19th century, is a much more complex device. When we use a phone, we don’t think about its structure and the processes occurring inside it. We simply use the functions provided by the phone's developers - buttons or touch screen to select a number and make calls. One of the first telephone interfaces was a knob that you turned to make a call. Of course, this was not very convenient. Nevertheless, the handle performed its function properly. If you look at the most modern and the very first telephone, you can immediately identify the most important details that are important both for a device from the late 19th century and for an ultra-modern smartphone. This is making a call (dialing a number) and receiving a call. Essentially, this is what makes a phone a phone and not something else. Now we have applied the principle in OOP - highlighting the most important characteristics and information about an object. This principle of OOP is called abstraction. Abstraction in OOP can also be defined as a way of representing elements of a real-world problem as objects in a program. Abstraction is always associated with the generalization of some information about the properties of objects or objects, so the main thing is to separate significant information from insignificant information in the context of the problem being solved. In this case, there can be several levels of abstraction. Let's try to apply the principle of abstraction to our phones. First, let's highlight the most common types of phones from the very first to the present day. For example, they can be represented in the form of a diagram shown in Figure 1. Principles of OOP - 2Now, with the help of abstraction, we can highlight general information in this hierarchy of objects: a common abstract type of objects - telephone, a general characteristic of the phone - the year of its creation, and a common interface - all phones are capable of receive and send calls. Here's what it looks like in Java:
public abstract class AbstractPhone {
    private int year;

    public AbstractPhone(int year) {
        this.year = year;
    }
    public abstract void call(int outputNumber);
    public abstract void ring (int inputNumber);
}
Based on this abstract class, we will be able to create new types of phones in the program using other basic Java OOP principles, which we will consider below.

Encapsulation

With the help of abstraction, we highlight what is common to all objects. However, each phone model is individual and somewhat different from others. How can we draw boundaries in the program and designate this individuality? How can we make sure that none of the users can accidentally or intentionally break our phone, or try to convert one model into another? For the world of real objects, the answer is obvious: you need to put all the parts into the phone body. After all, if we don’t do this and leave all the insides of the phone and the wires connecting them outside, there will definitely be an inquisitive experimenter who will want to “improve” the operation of our phone. To avoid such interference in the design and operation of an object, OOP uses the principle of encapsulation - another basic principle of OOP, in which the attributes and behavior of an object are combined in one class, the internal implementation of the object is hidden from the user, and an open interface is provided for working with the object. The programmer's job is to determine which attributes and methods will be publicly accessible and which are internal implementations of the object and should not be modified.

Encapsulation and Access Control

Let’s say that during production, information about it is engraved on the back of the phone: the year of its manufacture or the logo of the manufacturer’s company. This information quite specifically characterizes this model - its condition. We can say that the phone developer took care of the immutability of this information - it is unlikely that anyone would think of removing the engraving. In the Java world, the state of future objects is described in a class using fields, and their behavior is described using methods. The ability to change state and behavior is carried out using access modifiers to fields and methods - private, protected, public, and default(default access). For example, we decided that the year of creation, the name of the phone manufacturer, and one of the methods belong to the internal implementation of the class and cannot be changed by other objects in the program. Using code, the class can be described as follows:
public class SomePhone {

    private int year;
    private String company;
    public SomePhone(int year, String company) {
        this.year = year;
        this.company = company;
    }
private void openConnection(){
    //findComutator
    //openNewConnection...
}
public void call() {
    openConnection();
    System.out.println("I'm calling a number");
}

public void ring() {
    System.out.println("Дзынь-дзынь");
}

 }
A modifier privatemakes the fields and methods of a class available only within that class. This means that privatefields cannot be accessed from outside, nor can privatemethods be called. Hiding access to a method openConnectionalso leaves us with the opportunity to freely change the internal implementation of this method, since this method is guaranteed not to be used by other objects and will not disrupt their operation. To work with our object, we leave the methods open callusing ringthe modifier public. Providing public methods for working with an object is also part of the encapsulation mechanism, since if access to an object is completely denied, it will become useless.

Inheritance

Let's look at the phone chart again. You can see that it represents a hierarchy in which the model located below has all the characteristics of the models located higher on the branch, plus its own. For example, a smartphone uses a cellular network for communication (has the properties of a cell phone), is wireless and portable (has the properties of a cordless phone), and can receive and make calls (has the properties of a telephone). In this case, we can talk about inheritance of object properties. In programming, inheritance is the use of existing classes to define new ones. Let's look at an example of creating a smartphone class using inheritance. All cordless phones are powered by rechargeable batteries, which have a certain operating life in hours. So let's add this property to the wireless phones class:
public abstract class WirelessPhone extends AbstractPhone {

    private int hour;

    public WirelessPhone(int year, int hour) {
        super(year);
        this.hour = hour;
    }
    }
callCell phones inherit the properties of a wireless phone, we also added an implementation of the and methods to this class ring:
public class CellPhone extends WirelessPhone {
    public CellPhone(int year, int hour) {
        super(year, hour);
    }

    @Override
    public void call(int outputNumber) {
        System.out.println("Calling a number" + outputNumber);
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("A subscriber is calling you" + inputNumber);
    }
}
And finally, the smartphone class, which, unlike classic cell phones, has a full-fledged operating system. You can add new programs supported by this operating system to your smartphone, thus expanding its functionality. Using code, the class can be described as follows:
public class Smartphone extends CellPhone {

    private String operationSystem;

    public Smartphone(int year, int hour, String operationSystem) {
        super(year, hour);
        this.operationSystem = operationSystem;
    }
public void install(String program){
    System.out.println("Installing" + program + "For" + operationSystem);
}

}
As you can see, Smartphonewe created very little new code to describe the class, but we got a new class with new functionality. Using the OOP inheritance principle can significantly reduce the amount of code, and therefore make the programmer’s work easier.

Polymorphism

If we look at all phone models, then, despite the differences in the appearance and design of the models, we can identify some common behavior in them - they can all receive and make calls and have a fairly clear and simple set of control buttons. Applying one of the basic principles of OOP, which is already known to us, abstraction in programming terms, we can say that the phone object has one common interface. Therefore, phone users can quite comfortably use different models using the same control buttons (mechanical or touch), without going into the technical details of the device. So, you constantly use a cell phone, and you can easily make a call from its landline counterpart. The principle in OOP when a program can use objects with the same interface without information about the internal structure of the object is called polymorphism . Let's imagine that in our program we need to describe a user who can use any phone model to call another user. Here's how to do it:
public class User {
    private String name;

    public User(String name) {
        this.name = name;
            }

    public void callAnotherUser(int number, AbstractPhone phone){
// here it is polymorphism - using the abstract type AbstractPhone phone in the code!
        phone.call(number);
    }
}
 }
Now let's describe the different phone models. One of the first phone models:
public class ThomasEdisonPhone extends AbstractPhone {

public ThomasEdisonPhone(int year) {
    super(year);
}
    @Override
    public void call(int outputNumber) {
        System.out.println("Turn the Handle");
        System.out.println("Give me the phone number, sir");
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("Phone calls");
    }
}
Regular landline phone:
public class Phone extends AbstractPhone {

    public Phone(int year) {
        super(year);
    }

    @Override
    public void call(int outputNumber) {
        System.out.println("I'm calling a number" + outputNumber);
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("Phone calls");
    }
}
And finally, a cool video phone:
public class VideoPhone extends AbstractPhone {

    public VideoPhone(int year) {
        super(year);
    }
    @Override
    public void call(int outputNumber) {
        System.out.println("I connect a video channel for the subscriber" + outputNumber );
    }
    @Override
    public void ring(int inputNumber) {
        System.out.println("You have an incoming video call..." + inputNumber);
    }
  }
Let's create objects in the method main()and test the method callAnotherUser:
AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Andrey");
user.callAnotherUser(224466,firstPhone);
// Rotate the knob
// Tell me the number of the subscriber, sir
user.callAnotherUser(224466,phone);
//Call number 224466
user.callAnotherUser(224466,videoPhone);
//I connect the video channel for subscriber 224466
By calling the same method on the object user, we got different results. The selection of a specific method implementation callwithin a method callAnotherUserwas made dynamically based on the specific type of the calling object during program execution. This is the main advantage of polymorphism - the choice of implementation during program execution. In the phone class examples above, we used method overriding, a technique that changes the method implementation defined in the base class without changing the method signature. This is essentially a method replacement, and it is the new method defined in the subclass that is called when the program runs. Typically, when overriding a method, the annotation is used @Override, which tells the compiler to check the signatures of the overridden and overriding methods. As a result , to ensure that the style of your program complies with the concept of OOP and the principles of OOP java, follow these tips:
  • highlight the main characteristics of the object;
  • highlight common properties and behavior and use inheritance when creating objects;
  • use abstract types to describe objects;
  • Try to always hide methods and fields related to the internal implementation of the class.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION