Hi! Have you ever wondered why Java is designed exactly as it is? I mean, you declare classes and create objects based on classes, classes have methods, etc. But why is the language structured so that programs consist of classes and objects, and not something else? Why was the concept of an "object" invented and put at the forefront? Are all languages designed this way? If not, what advantages does it give to Java? Principles of object-oriented programming - 1As you can see, there are a lot of questions :) Let's try to answer each of them in today's lesson.

What is object-oriented programming (OOP)?

Of course, Java isn't made up of objects and classes just for fun. They aren't a whim of Java's creators, and not even their invention. There are many other languages based on objects. The first such language was called Simula. It was invented back in the 1960s in Norway. What's more, the concepts of a "class" and "method" appeared in Simula. By the standards of software development, Simula seems like an ancient language, but anyone can see its "family resemblance" with Java. You can probably easily read the code written in this language and explain in broad strokes what it does :)
Begin
	Class Rectangle (Width, Height); Real Width, Height;

	 Begin
	    Real Area, Perimeter;

	    Procedure Update;
	    Begin
	      Area := Width * Height;
              OutText("Rectangle is updating, Area = "); OutFix(Area,2,8); OutImage;
	      Perimeter := 2*(Width + Height);
              OutText("Rectangle is updating, Perimeter = "); OutFix(Perimeter,2,8); OutImage;
	    End of Update;

	    Update;
	    OutText("Rectangle created: "); OutFix(Width,2,6);
	    OutFix(Height,2,6); OutImage;
	 End of Rectangle;

       Rectangle Class ColouredRectangle (Color); Text Color;

	Begin
	    OutText("ColouredRectangle created, color = "); OutText(Color);
	    OutImage;
        End of ColouredRectangle;


      	 Ref(Rectangle) Cr;
	 Cr :- New ColouredRectangle(10, 20, "Green");
End;
This code sample code was taken from Simula - 50 years of OOP. As you can see, Java isn't so very different from its grandfather :) This is due to the fact that the appearance of Simula marked the birth of a new concept: object-oriented programming. Wikipedia defines OOP like this: "Object-Oriented Programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data, in the form of fields (often known as attributes), and code, in the form of procedures (often known as methods)." In my opinion, this is a really good definition. It wasn't long ago that you began to learn Java, but this definition probably doesn't contain any words you don't know :) Today OOP is the most common programming methodology. In addition to Java, OOP principles are used in many popular languages that you may have heard about. For example, C++ (actively used in game development), Objective-C and Swift (used to write programs for Apple devices), Python (most popular in machine learning), PHP (one of the most popular web development languages), JavaScript (it's easier to say what it isn't used for) and many others. So, what is are the "principles" of OOP anyway? We'll tell you in detail.

Principles of OOP

These are the foundation of the foundation. The 4 main features that together form the object-oriented programming paradigm. Understanding them is essential to becoming a successful programmer.

Principle 1. Inheritance

Good news: you already know some of the principles of OOP! :) We've already encountered inheritance a couple of times in lessons, and we managed to use it. Inheritance is a mechanism that lets you describe a new class based on an existing (parent) class. In doing so, the new class borrows the properties and functionality of the parent class. What is inheritance for and what advantages does it provide? Above all, code reuse. The fields and methods declared in parent classes can be used in descendant classes. If all types of cars have 10 common fields and 5 identical methods, you just need to move them into the Auto parent class. You can use them in descendant classes without any problems. Solid advantages: both quantitative (less code) and, as a result, qualitative (classes become much simpler). Moreover, inheritance is very flexible — you can add write separate functionality that the descendants are missing (some fields or behavior that are specific to a particular class). In general, as in real life, we all are somewhat similar to our parents, but also somehow different from them :)

Principle 2. Abstraction

This is a very simple principle. Abstraction means to identify the main, most significant characteristics of something, while simultaneously discarding anything minor and insignificant. No need to reinvent the wheel. Let's recall an example from an old lesson about classes. Suppose we're creating a filing system for company employees. To create "employee" objects, we've written an Employee class. What characteristics are important to describe them in the company filing system? Name, date of birth, SSN, and employee ID. But it's unlikely we'll need the employee's height, eye color, or hair color for this type of record. The company has no need for such information about an employee. So, in the Employee class, we declare the following variables: String name, int age, int socialSecurityNumber, and int employeeId. And we abstract away unnecessary information like eye color. However, if we're making a filing system for a modeling agency, the situation changes dramatically. A model's height, eye color, and hair color are important characteristics, but her SSN is absolutely irrelevant to us. So, in the Model class, we create the following variables: String height, String hair, String eyes.

Principle 3. Encapsulation

We've already run into this. In Java, encapsulation means restricting the ability to read and change data. As you can see, the term is based on the word "capsule". We'll use a "capsule" to hide some important data that we don't want others to change. Here's a simple example from real life. You have a first name and a last name. All your friends know them. But they don't have the ability to change your first or last name. We might say that the process to do that is "encapsulated" by the court system: you can change your last name only through the court clerk, and only you can do it. Other "users" have "read-only" access to your first and last name :) Another illustrative example is cash kept at home. Leaving it in plain sight in the middle of your room is not a good idea. Any "user" (person who comes to your house) will be able to change the amount of your money, i.e. they can take your money. It would be better to encapsulate it in a safe. Then access would be available only to you and only by using a special code. Obvious examples of encapsulation that you've already worked with are access modifiers (private, public, etc.), as well as setters and getters. If you don't encapsulate the Cat class's age field, then anyone can write:
Cat.age = -1000;
The encapsulation mechanism lets us protect the age field with a setter method, where we can ensure that age cannot be set to a negative number.

Principle 4. Polymorphism

Polymorphism is the ability to work with several types as if they were the same type. Moreover, the objects' behavior will be different depending on their type. Does that sound complicated? Let's make sense of it right now. Take the simplest example: animals. Create an Animal class with a single speak() method, and two subclasses — Cat and Dog.
public class Animal {

   public void speak() {

       System.out.println("Hello!");
   }
}

public class Dog extends Animal {

   @Override
   public void speak() {
       System.out.println ("Woof-woof!");
   }
}

public class Cat extends Animal {

   @Override
   public void speak() {
       System.out.println("Meow!");
   }
}
Now we'll try to declare an Animal reference variable and assign a Dog object to it.
public class Main {

   public static void main(String[] args) {

       Animal dog = new Dog();
       dog.speak();
   }
}
What method do you think will be called? Animal.speak() or Dog.speak()? The method in the Dog class will be called: Woof-woof! We created an Animal reference, but the object behaves like a Dog. If necessary, it could behave like a cat, horse, or some other animal. The important thing is to assign a specific subclass to the general Animal reference variable. This makes sense, because all dogs are animals. That's what we had in mind when we said "the objects' behavior will be different depending on their type." If we created a Cat object...
public static void main(String[] args) {

   Animal cat = new Cat();
   cat.speak();
}
the speak() method would display "Meow!" But what do we mean by 'the ability to work with several types as if they were the same type'? This is also pretty straightforward. Let's imagine that we're creating a barbershop for animals. Our barbershop should be able to give any animal a trim, so we create a trim() method with an Animal parameter (the animal getting a haircut).
public class AnimalBarbershop {

   public void trim(Animal animal) {

       System.out.println("The haircut is done!");
   }
}
And now we can pass Cat and Dog objects to the trim() method!
public static void main(String[] args) {

   Cat cat = new Cat();
   Dog dog = new Dog();

   AnimalBarbershop barbershop = new AnimalBarbershop();

   barbershop.trim(cat);
   barbershop.trim(dog);
}
And here's the clear example: the AnimalBarbershop class works with the Cat and Dog types as if they were the same type. At the same time, Cat and Dog have different behaviors: they each speak differently.

Why do we need OOP?

Why did OOP ever even arise as a new programming concept? Programmers had functioning tools, such as procedural languages. What prompted them to invent something fundamentally new? Above all, the complexity of the tasks that they faced. If 60 years ago the programmer's task was something like "evaluate some mathematical expression", now it could be something like "implement 7 different endings for the game S.T.A.L.K.E.R., depending on combinations of the player's decisions made at points A, B, C, D E, and F in the game." As you can see, the tasks have obviously become more complicated over the past decades. And as a result, the data types have become more complicated. This is another reason why OOP appeared. A mathematical expression can be evaluated easily using ordinary primitives. No objects are needed here. But the task with the game endings would be difficult to even describe without using custom classes. That said, it is quite easy to describe it using classes and objects. Obviously, we'll need several classes: Game, Stalker, Ending, PlayerDecision, GameEvent, and so on. In other words, even without starting to solve the problem, we can easily "sketch out" a solution in our head. The increasing complexity of the tasks forced programmers to divide them into parts. But this wasn't so easy to do in procedural programming. And quite often a program was like a tree with lots of branches representing all the possible execution paths. Depending on certain conditions, one branch of the program or another was executed. For small programs, this was convenient, but it was very difficult to divide a large problem into parts. This was yet another reason for the emergence of OOP. This paradigm gave programmers the ability to divide a program into a bunch of "modules" (classes), each of which does its own part of the work. By interacting with each other, all the objects accomplish the work of our program. In addition, we can reuse our code elsewhere in the program, which also saves a lot of time.