Oop is. OOP - what is it? Basic principles of object-oriented programming. Typing: static or dynamic

Abstract Data Types

The concept of abstract data types is key in programming. Abstraction implies separation and independent consideration of the interface and implementation.

Let's look at an example. We all watch television programs. Let's call the TV a module or object. This object has an interface with the user, i.e. controls (a set of buttons), image and sound reproduction. The more advanced the interface, the more convenient the TV is to use. We switch programs by pressing certain buttons, and at the same time we do not think about the physical processes occurring in the TV. Experts know about this. When we choose a TV, we are interested in its price and operational parameters, i.e. quality of image, sound, etc. However, we are not interested in what is inside. In other words, we return to the properties of the object (module), which are the interface and implementation. The main purpose of abstraction in programming is precisely to separate the interface from the implementation.

Let's return to our example. Suppose some subject is confident that he knows the structure of the TV well. He removes the lid and begins to “improve” it. Although sometimes this leads to some intermediate (local) successes, the final result is almost always negative. Therefore, such actions must be prohibited. In programming, this is supported by mechanisms for denying access or hiding internal components. Each object (module) is given the right to manage “its own property”, i.e. these functions and operations. Ignoring this principle violates the stability of the system and often leads to its complete destruction. The principle of abstraction requires the use of hiding mechanisms that prevent intentional or accidental modification of internal components.

Data abstraction involves defining and considering abstract data types(ATD) or, what is the same, new types of data entered by the user.

Abstract data type is a collection of data along with many operations that can be performed on this data.

Concept of object-oriented programming

According to the definition of an authority in the field of object-oriented methods of program development, Gradi Bucha, “object-oriented programming (OOP) is a programming methodology that is based on representing a program as a collection of objects, each of which is an implementation of a certain class (a special kind of type), and classes form a hierarchy on the principles of heritability.”

Object-oriented methodology, like the structural methodology, was created with the goal of disciplining the development process of large software systems and thereby reducing their complexity and cost.

An object-oriented methodology has the same goals as a structural methodology, but addresses them from a different starting point and, in most cases, allows you to manage more complex projects than a structural methodology.

As you know, one of the principles of project complexity management is decomposition. Gradi Booch distinguishes two types of decomposition: algorithmic (as he calls decomposition supported by structural methods) and object-oriented, the difference between which, in his opinion, is as follows: “The division by algorithms concentrates attention on the order of events occurring, and the division by objects gives special importance to the factors either causing actions or being the objects of application of these actions.”

In other words, algorithmic decomposition takes more into account the structure of relationships between parts of a complex problem, while object-oriented decomposition pays more attention to the nature of the relationships.

In practice, it is recommended to use both types of decomposition: when creating large projects, it is advisable to first use an object-oriented approach to create a general hierarchy of objects that reflect the essence of the programmable task, and then use algorithmic decomposition into modules to simplify the development and maintenance of the software package.

OO programming is undoubtedly one of the most interesting areas for professional program development.

Objects and classes

The basic blocks of an object-oriented program are objects and classes. In terms of content, an object can be represented as something felt or imagined and having well-defined behavior. Thus, an object can either be seen, or touched, or at least known that it is there, for example, represented in the form of information stored in computer memory. Let us define an object, adhering to the opinion of Gradi Bucha: “An object is a tangible entity that clearly manifests its behavior.”

An object - it is part of the reality around us, i.e. it exists in time and space (the concept of an object in programming was first introduced in the Simula language). Formally, the object is quite difficult to define. This can be done through some properties, namely: the object has state, behavior and can be uniquely identified (in other words, has a unique name).

Class - it is a set of objects that have a common structure and common behavior. A class is a description (abstraction) that shows how to construct a variable of this class that exists in time and space, called object. The meaning of the sentences “description of class variables” and “description of class objects” is the same.

An object has a condition, behavior and a passport (a means for its unambiguous identification); the structure and behavior of objects are described in classes, of which they are variables.

Let us now define the concepts state, behavior and identification object.

Object state combines all of its data fields (static component, i.e. unchanging) and the current values ​​of each of these fields (dynamic component, i.e. usually changing).

Behavior expresses the dynamics of changes in the states of an object and its reaction to incoming messages, i.e. how an object changes its states and interacts with other objects.

Identification(recognition) of an object is a property that allows you to distinguish an object from other objects of the same or other classes. Identification is carried out through a unique name (passport), which is assigned to an object in the program, just like any other variable.

It was already said above that the procedural (as well as modular) approach allows you to build programs consisting of a set of procedures (subroutines) that implement given algorithms. On the other hand, the object-oriented approach represents programs as a set of objects that interact with each other. Objects interact through messages. Let's assume our object is a circle. Then the message sent to this object could be: "draw yourself." When we say that a message is sent to an object, we are actually calling some function this object (function component). So, in the above example, we will call a function that will draw a circle on the display screen.

Basic principles of OOP

The basic principles of the object-oriented programming style include:

  • packaging or encapsulation;
  • inheritance;
  • polymorphism;
  • message transmission.

Packaging (encapsulation)

involves combining data and functions that manipulate this data in one object. Access to some data within the package may be either denied or restricted.

An object is characterized as a set of all its properties (for example, for animals - the presence of a head, ears, eyes, etc.) and their current values ​​(head - large, ears - long, eyes - yellow, etc.), so and the set of actions acceptable for this object (the ability to eat, sit, stand, run, etc.). The specified association in a single object as “material” components(head, ears, tail, paws) and actions that manipulate these parts (the "run" action moves the paws quickly) is called encapsulation.

In OOP, data are called object fields, and algorithms are called object methods.

Encapsulation allows you to isolate an object from its external environment to the maximum extent possible. It significantly increases the reliability of developed programs, because Algorithms localized in an object exchange relatively small amounts of data with the program, and the amount and type of this data is usually carefully controlled. As a result, replacing or modifying algorithms and data encapsulated in an object, as a rule, does not entail poorly traceable consequences for the program as a whole. Another important consequence of encapsulation is the ease of exchanging objects, transferring them from one program to another.

Inheritance

Both structural and object-oriented methodologies have the goal of constructing a hierarchical tree of relationships between objects (subtasks). But if the structural hierarchy is built according to simple principle dividing the whole into its component parts,

then when creating an object-oriented hierarchy, a different view of the same original object is adopted. An object-oriented hierarchy necessarily reflects the inheritance of properties of parent (overlying) object types to child (underlying) object types.

According to Gradi Booch, “inheritance is a relationship between objects in which one object repeats the structure and behavior of another.”

The principle of inheritance operates in life everywhere and every day. Mammals and birds inherit the characteristics of living organisms; unlike plants, the eagle and raven inherit a common property for birds - the ability to fly. On the other hand, lions, tigers, leopards inherit the “structure” and behavior characteristic of representatives of the order Felidae, etc.

Types at the top levels of an object-oriented hierarchy generally do not have concrete instances of objects. There is, for example, no specific living organism that is itself called a “mammal” or a “bird.” Such types are called abstract. Specific instances of objects, as a rule, have types of the lowest levels of the OO hierarchy: “Gena the crocodile” is a specific instance of an object of the “crocodile” type, “Matroskin the cat” is a specific instance of an object of the “cat” type.

Inheritance allows you to use class libraries and develop them (improve and modify library classes) in specific program. Inheritance allows you to create new objects by changing or adding properties to existing ones. The inheritor object receives all the fields and methods of the ancestor, but can add its own fields, add its own methods, or override inherited methods of the same name with its own methods.

The principle of inheritance solves the problem of modifying the properties of an object and gives OOP as a whole exceptional flexibility. When working with objects, a programmer usually selects an object that is closest in its properties to solving a specific problem, and creates one or more descendants from it that “can” do what is not implemented in the parent.

Consistent implementation of the “inherit and change” principle fits well with and greatly encourages an incremental approach to the development of large software projects.

When you build a new class by inheriting from an existing class, you can:

  • add new data components to the new class;
  • add new function components to the new class;
  • replace in the new class the function components inherited from the old class.

Polymorphism

allows you to use the same functions to solve different tasks. Polymorphism is expressed in the fact that under one name various actions are hidden, the content of which depends on the type of object.

Polymorphism is the property of related objects (that is, objects that have one common parent) to solve problems of similar meaning in different ways. For example, the action of “running” is common to most animals. However, each of them (lion, elephant, crocodile, turtle) performs this action in a different way.

With a traditional (non-object-oriented) approach to programming, the programmer will move the animals by calling a separate subroutine for a specific animal and a specific action.

Within the framework of OOP, the behavioral properties of an object are determined by the set of methods included in it, the programmer only indicates which object needs to perform which of its inherent actions, and (for the example under consideration) once described animal objects will move themselves in a way characteristic of them, using the included in its composition methods. By changing the algorithm of a particular method in the descendants of an object, the programmer can give these descendants specific properties that the parent does not have. To change a method, you need to override it in a child, i.e. declare a method of the same name in the descendant and implement the necessary actions in it. As a result, two methods of the same name will operate in the parent object and the child object, having a different algorithmic basis and, therefore, giving the objects different properties. This is called object polymorphism.

Thus, in our example with animal objects, the action “run” will be called a polymorphic action, and the variety of forms of manifestation of this action will be called polymorphism.

Description of the object type

A class or object is a data structure that contains fields and methods. Like any data structure, it begins with a reserved word and ends with the operator end. The formal syntax is not complicated: a description of an object type is obtained by replacing the word in the record description record on word object or class and add declaration of functions and procedures above the fields.

Type<имя типа объекта>= object
<поле>;
<поле>;
….
<метод>;
<метод>;
end ;

There is a special reserved word in ObjectPascal class for describing objects, borrowed from C++.

Type<имя типа объекта>= class
<поле>;
….
<метод>;
<метод>;
end ;

ObjectPascal supports both models of object description.

An object component is either a field or a method. The field contains the name and data type. A method is a procedure or function declared inside an object type declaration, including special procedures that create and destroy objects (constructors and destructors). A method declaration within an object type declaration consists only of a header. This is a type of preliminary description of a subroutine. The body of the method follows the object type declaration.

Example. An object type "ancestor" is introduced, which has a Name data field and can perform two actions:

  • proclaim: “I am an ancestor!”;
  • give your name.

Type tPredoc = object Name: string ; (object data field)
Procedure Declaration ; (declaration of object methods)
Procedure MyName ;
End ;

The texts of subroutines that implement object methods must be given in the section describing procedures and functions. The headers when describing the method implementation repeat the headers given in the type description, but are supplemented by the object name, which is separated from the procedure name by a dot. In our example:

Procedure tPredoc.Declaration ; (object method implementation)
begin
writeln("I am an ancestor!");
end ;
Procedure tPredoc.MyName ; (object method implementation)
begin
writeln("I am", Name);
end;

Inside the method descriptions for fields and methods of this type referred to simply by name. So the MyName method uses the Name field without explicitly indicating its ownership of the object, as if the implicit with statement were executed<переменная_типа_объект>do.

Objects also mean variables of an object type - they are called copies. Like any variable, an instance has a name and a type: they must be declared.

…….(declaration of an object type and description of its methods)
var v 1: tPredoc ; (object instance declaration)
begin
v1. Name:= "Petrov Nikolai Ivanovich";
v1.Declaration;
v1.MyName
end.

Using a v1 object's data field is the same syntax as using record fields. Calling methods of an object instance means that the specified method is called with the data of the object v 1. As a result, the lines will be displayed on the screen

I am an ancestor!
I am Nikolai Ivanovich Petrov

Similar to records, fields of object type variables can be accessed using either qualified identifiers or a with statement.

For example, in the text of the program, instead of operators

it is possible to use a with operator of this type

with v1 do
begin
Name:= "Petrov Nikolai Ivanovich";
Declaration ;
MyName
End ;

Moreover, using the with statement with object types, as well as for records, is not only possible, but also recommended.

Type hierarchy (inheritance)

Types can be arranged in a hierarchy. An object can inherit components from another object type. An inheritor object is a child. The object that is inherited is the ancestor. We emphasize that inheritance only applies to types, not to instances of objects.

If an object type (ancestor, parent) is introduced, and it needs to be supplemented with fields or methods, then a new type is introduced, declared a successor (descendant, child type) of the first one, and only new fields and methods are described. The descendant contains all the fields of the ancestor type. Note that the fields and methods of the ancestor are available to the child without special instructions. If the descendant description repeats the names of the ancestor's fields or methods, then the new descriptions override the ancestor's fields and methods.

OOP always starts with a base class. This is the template for the base object. The next step is to define a new class, which is called a derived class and is an extension of the base one.

A derived class can include additional methods that do not exist in the base class. It can redefine methods (or even remove them entirely).

A derived class should not override all methods of the base class. Each new object inherits the properties of the base class; you only need to define those methods that are new or have been changed. All other methods of the base class are considered part of the derived class. This is convenient because... when a method is changed in the base class, it is automatically changed in all derived classes.

The inheritance process can continue. A class that is derived from a base class can itself become a base class for other derived classes. In this way, OO programs create a class hierarchy.

Most often, the structure of a class hierarchy is described as a tree. The tops of the tree correspond to classes, and the root corresponds to a class that describes something common (most common) to all other classes.

Inheritance by child types of information fields and methods of their parent types is carried out according to the following rules.

Rule 1. Information fields and methods of a parent type are inherited by all its child types, regardless of the number of intermediate levels of the hierarchy.

Rule 2. Access to fields and methods of parent types within the definition of any child types is performed as if they were described in the child type itself.

Rule 3. No child type can use the field identifiers of its parent types.

Rule 4. A child type can define an arbitrary number of its own methods and information fields.

Rule 5. Any change to the text in a parent method automatically affects all methods of the child types that call it.

Rule 6. In contrast to information fields, method identifiers in child types can be the same as method names in parent types. In this case, the child method is said to override (suppress) the parent method of the same name. Within a child type, when specifying the name of such a method, the child method will be called, and not the parent.

Let's continue with our example. In addition to the tPredoc ancestor type we introduced, we can introduce descendant types:

ture tSon= object(tPredoc) (Type that inherits tPredoc)
procedure Declaration; (overlapping ancestor methods)
procedure My Name(Predoc: tPredoc);
end ;

Type tGrandSon=object(tSon) (Type inheriting from tSon)
procedure Declaration ; (overlapping ancestor methods)
end ;

The name of the ancestor type is given in parentheses after the word object. We have created a hereditary hierarchy of three types: tSon (“son”) is the heir to the tPredoc type, and the tGrandSon (“grandson”) type is the heir to the tSon type. The tSon type overrides the Declaration and My N a m e methods, but inherits the Name field. The tGrandSon type overrides only the Declaration method and inherits the Name field from its common ancestor, and the overridden Declaration method from its immediate ancestor (type tSon).

Let's figure out what exactly we want to change in the parent methods. The fact is that the “son” must proclaim somewhat differently than his ancestor, namely, say “I am the father!”

procedure tSon.Declaration ; (implementation of methods of descendant objects)
begin
writeln("I am the father!");
end;

And when giving his name, the “son” must provide the following information:

  • I<фамилия имя отчество >
  • I am the son<фамилия имя отчество своего предка>

procedure tSon .My Name (predoc: tPredoc);
begin
inherited Mu Name; (call immediate ancestor method)
writeln("I am the son", predoc.Name, "a");
end;

In our example, the descendant tSon from the My Name method calls the method of the same name on its immediate ancestor type tPredoc. This call is provided by the directive inherited, followed by the immediate ancestor method being called. If there is a need to call a method of a distant ancestor in some child type at any level of the hierarchy, then this can be done using a qualified identifier, i.e. indicate explicitly the type name of the parent object and, separated by a dot, the name of its method:

Now let's deal with the "grandson". The method in which the "grandson" says its name is exactly the same as its immediate ancestor (type tSon), so there is no need to override this method, it is better to automatically inherit this method and use it as your own. But in the Declaration method you need to declare “I am a grandson!”, so the method will have to be redefined.

procedure tGrandSon.Declaration;
begin
writeln("I am a grandson!");
end;

Let's consider an example of a program in which we define an instance of the tPredoc type, call it “grandfather”, an instance of the tSon type “father”, and an instance of the tGrandSon type “grandson”. Let's ask them to introduce themselves.

Example program using OOP

(program title)
……………….
(section of descriptions of types, including object types tPredoc, tSon, tGrandSon)
(Note! Instances of object types can be described as typed constants, which is what we have done below for the example)
const ded: tPredoc = (Name: "Nikolai Ivanovich Petrov");
otec: tSon = (Name: "Petrov Sergey Nikolaevich");
vnuk: tGrandSon = (Name: "Petrov Oleg Sergeevich");
(section of descriptions of procedures and functions, where all methods declared in object types must be written)
begin
ded.Declaration; (calling common ancestor methods)
ded.My Name;
writeln;
otec.Declaration;
otec.MyName(ded); (calling methods of an otec object of type tSon)
writeln;
vnuk.Declaration; (calling methods of a vnuk object of type tGrandSon)
vnuk.MyName(otec);
end.

Our program will display:

An example of displaying the result

I am an ancestor!
I am Nikolai Ivanovich Petrov

I am the father!
I am Petrov Sergey Nikolaevich
I am the son of Petrov Nikolai Ivanovich

I am a grandson!
I am Petrov Oleg Sergeevich
I am the son of Sergei Nikolaevich Petrov

Please note that in the procedure header tSon. MyName is given a tPredoc data type as a parameter, and when using this procedure, variables of both tPredoc and tSon types are passed to it. This is possible because the ancestor is type compatible with its descendants. The opposite is not true. If we replace tSon in the procedure header. MyName when describing parameters of type tPredoc on tSon , the compiler will indicate type incompatibility when using the ded variable in the otec line. MyName(ded).

Polymorphism and virtual methods

Polymorphism– this is the property of related objects (i.e. objects that have the same parent) to solve problems of similar meaning in different ways.

Two or more classes that are derived from the same base class are called polymorphic. This means that they can have common characteristics, but also have their own properties.

Within OOP, the behavioral properties of an object are determined by the set of methods included in it. By changing the algorithm of a particular method in the descendants of an object, the programmer can give these descendants specific properties that the parent does not have. To change a method, you need to override it in a child, i.e. declare a method of the same name in the descendant and implement the necessary actions in it. As a result, two methods of the same name will operate in the parent object and the child object, having a different algorithmic basis and, therefore, giving the objects different properties. This is called object polymorphism.

In the example discussed above, all three object types tPredoc, tSon and tGrandSon have the same methods Declaration and MyName. But the tSon object type implements the MyName method slightly differently than its ancestor. And all three Declaration methods of the same name are executed differently for each object.

Object methods are static, virtual and dynamic.

Static methods

included in the program code during compilation. This means that before using the program it is determined which procedure will be called at a given point. The compiler determines what type of object is being used in a given call and substitutes a method for that object.

Objects different types may have static methods of the same name. In this case, the required method is determined by the type of the object instance.

This is convenient, since methods of different types of objects that have the same meaning can be named the same, and this simplifies the understanding of both tasks and programs. Static overlap is the first step of polymorphism. Identical names are a matter of programming convenience, not a principle.

Virtual methods

unlike static ones, they are connected to the main code at the stage of program execution. Virtual methods provide the ability to determine the type and instantiate an instance of an object at runtime, and then call methods on that object.

This fundamentally new mechanism, called late binding, provides polymorphism, i.e. different way behavior for different but homogeneous (in the sense of inheritance) objects.

The description of a virtual method differs from the description of a regular method by adding a function word after the method header virtual .

procedure Method (parameter list); virtual;

The use of virtual methods in the object type hierarchy has certain limitations:

  • if a method is declared as virtual, then in a descendant type it cannot be overridden by a static method;
  • objects that have virtual methods are initialized by special procedures, which, in essence, are also virtual and are called constructor ;
  • lists of variables and function types in the headers of overlapping virtual procedures and functions must match completely;

Usually on constructor the work of initializing an object instance is assigned: assigning initial values ​​to fields, initial display on the screen, etc.

In addition to the actions included in it by the programmer, the designer prepares the mechanism late binding virtual methods. This means that before any virtual method is called, some constructor must be executed.

A constructor is a special method that initializes an object containing virtual methods. The constructor header looks like this:

constructor Method(parameter list);

Reserved word constructor replaces the words procedure and virtual .

The main and special purpose of the constructor is to establish connections with the virtual method table (VMT) - a structure containing references to virtual methods. Thus, the constructor initializes the object by establishing a connection between the object and the VMT with the addresses of the virtual method codes. Late binding occurs during initialization.

Each object has its own VMT virtual method table. This is what allows the method of the same name to call different procedures.

Having mentioned the constructor, we should also say about destructor. Its role is the opposite: perform actions that complete work with the object, close all files, clear dynamic memory, clear the screen, etc.

The destructor header looks like this:

destructor Done ;

The main purpose of destructors is to destroy VMT of this object. Often the destructor does nothing else and is an empty procedure.

destructor Done ;
begin end ;

essentially used the prescriptive programming paradigm - the goal was to create code that acted appropriately on the data. This approach is good for solving small problems, but it creates many intractable problems when trying to create large software systems .

One of the alternatives directive programming is object-oriented programming, which really helps to cope with the nonlinearly growing complexity of programs as their volume increases. One should not, however, conclude that using the object-oriented programming paradigm guarantees a successful solution to all problems.

In order to become a professional in programming, you need talent, creativity, intelligence, knowledge, logic, the ability to build and use abstractions and, most importantly, experience.

In this section, we will continue our introduction to the basic concepts of object-oriented programming, which we began in the first chapter of the book. First, OOP concepts common to various programming languages ​​will be discussed, and then their implementation in the Java language.

You should be aware that the course on object-oriented programming is taught to undergraduate students over the course of an entire semester, and therefore the material presented below represents only a very basic introduction to the world of OOP. A much more complete treatment of many of the issues related to object-oriented design, engineering, and programming is contained in the book, and in the third chapter of the book you can find a very clear description of all object-oriented aspects of the Java language.

Basic OOP Concepts

Object-oriented programming or OOP (object-oriented programming) - programming methodology based on the representation of a program as a collection of objects, each of which is an implementation of a certain type, using a mechanism forwarding messages and classes organized in inheritance hierarchy.

The central element of OOP is abstraction. Data is converted into objects using abstraction, and the sequence of processing this data turns into a set of messages passed between these objects. Each of the objects has its own unique behavior. Objects can be treated as concrete entities that respond to messages commanding them to perform some action.

OOP is characterized by the following principles (according to Alan Kay):

  • everything is an object;
  • calculations are carried out through interaction (data exchange) between objects, in which one object requires another object to perform some action; objects interact by sending and receiving messages; a message is a request to perform an action, supplemented by a set of arguments that may be needed when performing the action;
  • each object has an independent memory, which consists of other objects;
  • each object is a representative of a class that expresses the general properties of objects of a given type;
  • set in class functionality(object behavior); thus, all objects that are instances of the same class can perform the same actions;
  • classes are organized into a single tree structure with a common root, called inheritance hierarchy; the memory and behavior associated with instances of a particular class are automatically available to any class lower in the hierarchical tree.

Definition 10.1. Abstraction- a method of solving a problem in which objects of different kinds are combined general concept(concept), and then the grouped entities are considered as elements of a single category.

Abstraction allows you to separate the logical meaning of a program fragment from the problem of its implementation, dividing external description(interface) of an object and its internal organization(implementation).

Definition 10.2. Encapsulation- a technique in which information that is insignificant from the point of view of the object’s interface is hidden inside it.

Definition 10.3. Inheritance- a property of objects through which instances of a class gain access to the data and methods of ancestor classes without redefining them.

Inheritance allows different data types to share the same code, resulting in smaller code and greater functionality.

Definition 10.4.

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. Let'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 what you have developed new model phone and want to set up 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.

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 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 the object. This principle 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. Now, using abstraction, we can highlight general information in this hierarchy of objects: the general abstract type of objects is telephone, general characteristics phone - the year of its creation, and the common interface - all phones are capable of receiving and sending 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) ; ) On 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

By using abstractions we highlight general for 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- his 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 ("Calling the number") ; ) public void ring () ( System. out. println ("Ding-ding") ; ) ) Modifier private makes the fields and methods of a class available only within that class. This means that you can access private fields from outside is impossible, just as there is no way to call private methods. Hiding access to the openConnection method also leaves us free to change the internal implementation of this method, since this method is guaranteed not to be used by other objects and will not disrupt their work. To work with our object, we leave the call and ring methods open using a 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 cordless phone) and can receive and make calls (using phone properties). 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 operate from batteries, which have a certain service life in hours. So let's add this property to the cordless phone class: public abstract class WirelessPhone extends AbstractPhone ( private int hour; public WirelessPhone (int year, int hour) ( super (year) ; this . hour = hour; ) ) Cell phones inherit the properties of a cordless phone, We also added an implementation of the call and ring methods to this class: public class CellPhone extends WirelessPhone ( public CellPhone (int year, int hour) ( super (year, hour); ) @Override public void call (int outputNumber) ( System. out. println ("Calling 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 ("I install " + program + "for" + operationSystem) ; ) ) As you can see, we created very little new code to describe the Smartphone class, but we got a new class with new functionality. Using this principle of OOP java 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 cell phone, and you can easily make a call from its landline counterpart. A principle in OOP when a program can use objects with the same interface without information about internal structure 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 you can do it: public class User ( private String name; public User (String name) ( this . name = name; ) public void callAnotherUser (int number, AbstractPhone phone) ( // this is polymorphism - using the abstract type AbstractPhone phone in the code! phone. call (number) ; ) ) ) Now we will describe the various 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 ("Rotate the knob" ) ; System. out .println( "Please provide your phone number, sir") ; ) @Override public void ring (int inputNumber) ( System. out. println ( "The phone is ringing" ) ; ) ) Normal landline phone: public class Phone extends AbstractPhone ( public Phone (int year) ( super (year) ; ) @Override public void call (int outputNumber) ( System. out. println ("Calling number" + outputNumber) ; ) @Override public void ring (int inputNumber) ( System. out. println ( "The phone is ringing" ) ; ) ) 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 am connecting 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 main() method and test the callAnotherUser method: 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 //Please provide the subscriber's number, sir user. callAnotherUser(224466, phone); //Calling number 224466 user. callAnotherUser(224466, videoPhone); //Connecting a video channel for subscriber 224466 By calling the same method on the user object, we got different results. The specific implementation of the call method within the callAnotherUser method was selected 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 @Override annotation is used, which tells the compiler to check the signatures of the overridden and overriding methods. Eventually To ensure that the style of your program matches 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.
English version of this post:

(as OOP stands for) is, first of all, a programming paradigm.
Programming Paradigm defines how the programmer sees the execution of the program.
Thus, the OOP paradigm is characterized by the fact that the programmer views the program as a set of interacting objects, while, for example, in functional programming the program is represented as a sequence of function calculations. Procedural programming, or, as it is also correctly called, classical operational programming, involves writing an algorithm to solve a problem; in this case, the expected properties of the final result are not described or indicated. Structured programming basically follows the same principles as procedural programming, with a few useful techniques added to it.
Non-procedural programming paradigms, which include the object-oriented paradigm, have completely different ideas.
Gradi Bucha's definition reads: “ Object-oriented programming is a programming methodology that is based on representing a program as a collection of objects, each of which is an implementation of a specific class (a special kind of type), and the classes form a hierarchy based on the principles of heritability.”
Structured and object-oriented programming are based on the scientific method known as decomposition- a method that uses the structure of the problem and allows you to break the solution of a common large problem into solving a sequence of smaller problems. Decomposition of OOP occurs not according to algorithms, but according to objects used in solving the problem. This decomposition reduces the size of software systems by reusing common mechanisms. It is known that systems visual programming or systems built on object-oriented programming principles are more flexible and evolve more easily over time.

History of the development of OOP originates in the late 60s. The first object-oriented language was the Simula programming language, created at a computer center in Norway. The language was intended to model real-world situations. A special feature of Simula was that the program written in the language was organized into programming objects. Objects had instructions called methods and data called variables; methods and data determined the behavior of the object. During the simulation, the object behaved according to its default behavior and, if necessary, changed the data to reflect the impact of the action assigned to it.

Today there is a sufficient number object-oriented programming languages, the most popular of which are currently C++, Delphi, Java, Visual Basic,Flash. But, in addition, many languages ​​that are usually classified as a procedural paradigm also have OOP properties, being able to work with objects. Thus, object-oriented programming in C is a large section of programming in this language, the same applies to OOP in python and many other structured languages.

When talking about OOP, another definition often comes up - visual programming. It additionally provides extensive use of object prototypes, which are defined as object classes.
Events. Many visual programming environments implement a characteristic (in addition to encapsulation, polymorphism and inheritance) of an object - an event. Events in object-oriented programming are the ability to process so-called messages (or events) received from the operating system. Windows systems or the program itself. This principle is typical for all components of the environment that process various events that arise during program execution. Essentially, an event is some action that activates a standard reaction of an object. An event can be considered, for example, a click on a mouse button, hovering the mouse cursor over a menu item, opening a tab, etc. The order in which certain actions are performed is determined precisely by the events that occur in the system and the reaction of objects to them.
Classes and objects in OOP- various concepts. The concept of a class in OOP is a data type (the same as, for example, Real or String), and an object is a specific instance of a class (its copy), stored in computer memory as a variable of the corresponding type.
Class is structural type data. The class includes a description of data fields, as well as procedures and functions that operate on these data fields. OOP method- these are such procedures and functions in relation to classes.
Classes have fields (like the record data type), properties that are similar to fields, but have additional descriptors that define the mechanisms for writing and reading data, and methods - subroutines that are aimed at changing the fields and properties of the class.

Basic principles of OOP

The principles of object-oriented programming, in addition to event handling, are encapsulation, inheritance, subclassing and polymorphism. They are especially useful and necessary when developing applications that are replicable and easy to maintain.
An object combines methods and properties that cannot exist separately from it. Therefore, if an object is deleted, its properties and associated methods are deleted. When copying, the same thing happens: the object is copied as a whole. OOP Encapsulation- this is the described characteristic.

OOP inheritance principle and subclasses

Absolutely all objects are created on the basis of classes, and they inherit the properties and methods of these classes. In turn, classes can be created on the basis of other classes (parents), then such classes are called subclasses (descendants). Subclasses inherit all properties and methods of the parent class. In addition, for a subclass or descendant class, you can define new, your own, properties and methods, as well as change the methods of the parent class. Changes to properties and methods of a parent class are tracked in subclasses created on the basis of this class, as well as in objects created on the basis of subclasses. This is what OOP inheritance is all about.

OOP polymorphism

In object-oriented programming, polymorphism is characterized as the interchangeability of objects with the same interface. This can be explained this way: a child class inherits instances of the methods of the parent class, but the execution of these methods can occur in a different way, corresponding to the specifics of the child class, that is, modified.
That is, if in procedural programming the name of a procedure or function uniquely identifies the executed code related to this procedure or function, then in object-oriented programming you can use the same method names to perform different actions. That is, the result of executing the same method depends on the type of object to which the method is applied.

The site presents a partial theory of object-oriented programming for beginners and OOP examples of problem solving. OOP lessons on the site are detailed algorithms for completing a given task. Based on the completion of these laboratory works, the student will be able to independently solve other similar problems in the future.
We wish you an easy and interesting learning of object-oriented programming!

Object-oriented programming (OOP) forms the basis of Java. Essentially, all Java programs are object-oriented to some extent. The Java language is so closely related to OOP that before you start writing even the simplest programs in it, you should first become familiar with the basic principles of OOP. Therefore, let's start by considering the theoretical issues of OOP.

Two techniques

All computer programs consist of two elements: code and data. Moreover, a program can conceptually be organized around its code or its data. In other words, the organization of some programs is determined by “what happens,” while others are determined by “what is influenced.” There are two methods for creating programs. The first of them is called process-oriented model and characterizes a program as a sequence of linear steps (i.e., code). The process-oriented model can be viewed as code that acts on the data. This model has been used quite successfully in procedural languages ​​like C. But, as noted in Chapter 1, this approach raises a number of difficulties due to the increasing size and complexity of programs.

In order to overcome the increase in program complexity, development of an approach called object-oriented programming. Object-oriented programming allows you to organize a program around its data (that is, objects) and a set of well-defined interfaces with this data. An object-oriented program can be characterized as data that controls access to code. As will be discussed later, outsourcing data management functions can provide several organizational benefits.

Abstraction

An important element of OOP is abstraction. It is human nature to represent complex phenomena and objects by resorting to abstraction. For example, people imagine a car not as a collection of tens of thousands of individual parts, but as a completely specific object that has its own special behavior. This abstraction allows you to avoid having to think about the complexity of the parts that make up a car when, say, driving to the store. You can ignore the details of the engine, gearbox and brake system. Instead, the object can be used as a single unit.

Hierarchical classifications are an effective means of applying abstraction. This makes it possible to simplify the semantics of complex systems, breaking them down into more manageable parts. Externally, the car looks like a single object. But once you look inside, it becomes clear that it consists of several subsystems: steering, brakes, audio system, seat belts, heater, navigator, etc. Each of these subsystems, in turn, is assembled from more specialized units. For example, an audio system consists of a radio, CD player and/or audio cassettes. The point of all this is that the structure of a car (or any other complex system) can be described using hierarchical abstractions.

Hierarchical abstractions of complex systems can also be applied to computer programs. Through abstraction, the data of a traditional process-oriented program can be transformed into its constituent objects, and the sequence of process steps can be transformed into a set of messages passed between these objects. Thus, each of these objects describes its own specific behavior. These objects can be considered concrete entities that respond to messages instructing them explain a specific action. This, in fact, is the whole essence of OOP.

OOP principles underlie both the Java language and human perception of the world. It is important to understand how these principles are implemented in programs. As will become clear later, OOP is another, but more effective and natural technique for creating programs that can survive the inevitable changes that accompany life cycle any major software project, including its conception, development, and maturation. For example, if you have carefully defined objects and clear, reliable interfaces to those objects, you can safely and easily remove or replace parts of the old system.

Three principles of OOP

All object-oriented programming languages ​​provide mechanisms to facilitate the implementation of the object-oriented model. These mechanisms are encapsulation, inheritance and polymorphism. Let's look at these OOP principles individually.

Encapsulation

The mechanism that links code and the data it manipulates, protecting both from outside interference and abuse, is called encapsulation. Encapsulation can be thought of as a protective shell that protects code and data from random access by other code outside the shell. Access to code and data inside the shell is strictly controlled by a carefully defined interface. To make a real-world analogy, consider an automatic transmission in a car. It encapsulates a lot of information about the car, including the amount of acceleration, the steepness of the surface on which it is driving, and the position of the gearshift lever. The user (in this case the driver) can influence this complex encapsulation in only one way: by moving the gearshift lever. The gearbox must not be influenced, for example, by the turn indicator or windshield wipers. Thus, the gearshift lever is a strictly defined, and essentially the only, interface with the gearbox. Moreover, what happens inside the gearbox does not affect objects outside it. For example, changing gears does not turn on the headlights! Function automatic switching gears are encapsulated, so dozens of car manufacturers can implement it however they want. But from the driver's point of view, all these gearboxes work the same. A similar principle can be applied in programming. Strength encapsulated code is that everyone knows how to access it, and therefore can be used regardless of implementation details and without fear of unexpected side effects.

The core of ejava encapsulation is the class. Classes will be discussed in more detail in subsequent chapters, but until then it is useful to give at least a brief description of them. Class defines the structure and behavior (data and code) that will be shared by a set of objects. Each object of a given class contains structure and behavior that are defined by the class, as if the object were “cast” in the mold of the class. Therefore, sometimes objects are called instances of the class. Thus, a class is a logical construct, and an object is its physical embodiment.

When you create a class, you define the code and data that make up the class. Together these elements are called members class. In particular, the data defined in a class is called change their members, or instance variables, and the code that operates on the data is member methods or simply methods.(What Java programmers call methods, C/C++ programmers call functions) In programs properly written in Java, methods define how member variables are used. This means that the behavior and interface of a class are determined by the methods that operate on the data of its instance.

Because the purpose of a class is to encapsulate complex structure programs, there are mechanisms for hiding the complex implementation structure in the class itself. Each method or variable in a class can be marked as private or public. Open The interface of a class represents everything that external users of the class should or can know. Closed methods and data can only be accessed by code that is a member of a given class. Therefore, any other code that is not a member of this class cannot access the private method or variable. Private members of a class are accessible to other parts of the program only through public methods of the class, and thereby eliminate the possibility of performing illegal actions. This, of course, means that the public interface must be carefully designed and should not reveal unnecessary details about the internal workings of the class (Figure 1).

Rice. 1.

Inheritance

The process by which one object acquires the properties of another is called inheritance. This is a very important principle of OOP because inheritance provides the principle of hierarchical classification. As noted earlier, most knowledge becomes accessible to assimilation through hierarchical (i.e., top-down) classification. For example, golden retriever is part of the classification dogs, which, in turn, belongs to the class mammals, and that one - to an even greater class animals. Without hierarchies, each object would have to explicitly define all its characteristics. But thanks to inheritance, an object must define only those things that make it special in the class. An object can inherit common attributes from its parent object. Thus, the inheritance mechanism allows you to make one object a special case of a more general case. Let's consider this mechanism in more detail.

As a rule, most people perceive the world around them in the form of hierarchically interconnected objects, like animals, mammals and dogs. If you want to give an abstract description of animals, you can say that they have certain properties: size, level of intelligence and skeletal system. Animals also have certain behavioral characteristics: they eat, breathe and sleep. This description of properties and behavior constitutes a definition class animals.

If we were to describe a more specific class of animals, such as mammals, we would need to specify more specific properties, such as the type of teeth and mammary glands. This definition is called subclass animals that belong to superclass(parent class) mammals. And since mammals are only more precisely defined animals, they inherit all the properties of animals. Lower level subclass class hierarchy inherits all the properties of each of its parent classes (Fig. 2).

Rice. 2.

Inheritance is also related to encapsulation. If a single class encapsulates certain properties, then any subclass of it will have the same properties plus any additional ones that determine its specialization (Fig. 3). Because of this key principle, the complexity of object-oriented programs increases in arithmetic rather than geometric progression. The new subclass inherits the attributes of all its parent classes and therefore does not have unpredictable interactions with much of the rest of the system code.

Rice. 3. Labradorite fully inherits the encapsulated properties of all parent animal classes

Polymorphism

Polymorphism(from the Greek “many forms”) is an OOP principle that allows the same interface to be used for a common class of actions. Each action depends on the specific situation. Consider, as an example, a stack that acts as a reverse store list. Let's say a program requires three types of stacks: for integer values, for floating-point numeric values, and for characters. The implementation algorithm for each of these stacks remains the same, despite differences in the data that is stored in them. In a non-object-oriented language, manipulating the stack would require creating three different sets of utilities under separate names. In java, thanks to the principle of polymorphism, a common set of utilities can be defined for handling the stack under the same common names.

More generally, the principle of polymorphism is often expressed by the phrase “one interface, multiple methods.” This means that it is possible to develop a common interface for a group of activities that are related together. This approach reduces the complexity of the program, since the same interface is used to specify general class of actions. And the choice specific action(i.e. method) is done in relation to each situation and is the responsibility of the compiler. This saves the programmer from having to make these choices manually. He just needs to remember the general interface and apply it correctly.

If we continue the analogy with dogs, we can say that a dog’s sense of smell is a polymorphic property. If a dog smells a cat, it will bark and chase it. And if the dog smells his food, he will begin to salivate and will rush to his bowl. In both cases the same sense of smell operates. The only difference is what exactly emits the smell, i.e. in the data type affecting the dog's nose! This general principle can be implemented by applying it to methods in a Java program.

Combined use of polymorphism, encapsulation and inheritance

When the principles of polymorphism, encapsulation, and inheritance are applied correctly, they together form a programming environment that supports the development of programs that are more robust and scalable than would be the case with a process-oriented model. A carefully designed class hierarchy provides a strong foundation for reusing code that you've spent time and effort developing and testing. Encapsulation allows you to revert to previously created implementations without breaking code that depends on open interface classes used in the application. And polymorphism allows you to create clear, practical, readable and stable code.

From the two examples given earlier from real life The example of cars more fully illustrates the capabilities of OOP. While the example of dogs is quite suitable for considering OOP from an inheritance point of view, the example of cars has more in common with programs. Getting behind the wheel various types(subclasses) of cars, all drivers use inheritance. Whether the vehicle is a school bus, a passenger car, a sports car or a family minivan, all drivers will be able to easily find and operate the steering wheel, brakes, and accelerator pedal. With a little fiddling with the gear lever, most people can even appreciate the differences between a manual transmission and an automatic, as long as they have a clear understanding of the two's common parent class, the transmission system.

When using cars, people constantly interact with their encapsulated characteristics. The brake and gas pedals hide the incredible complexity of the corresponding objects behind an interface so simple that to control these objects you just need to press the pedal with your foot! The specific motor implementation, brake type, and tire size have no effect on how they interact with pedal class determination.

Finally, polymorphism clearly reflects the ability of automobile manufacturers to offer a wide variety of options for essentially the same vehicle. For example, the vehicle can be equipped with anti-lock brakes or traditional brakes, power steering or rack and pinion steering, and 4-, 6-, or 8-cylinder engines. But in any case, you will have to press the brake pedal to stop, turn the steering wheel to turn, and press the accelerator pedal to make the car move faster. The same interface can be used to control a wide variety of implementations.

As you can see, thanks to the combined application of the principles of encapsulation, inheritance and polymorphism, individual parts can be turned into an object called a car. The same applies to computer programs. OOP principles make it possible to construct a coherent, reliable, maintainable program from many separate parts.

As noted at the beginning of this section, every Java program is object-oriented. More precisely, every Hajava program applies the principles of encapsulation, inheritance, and polymorphism. At first glance, it may seem that not all of these principles appear in the short example programs given in the rest of this chapter and a number of subsequent chapters, but they are nevertheless present in them. As will become clear later, many Java language features are part of built-in class libraries that make extensive use of the principles of encapsulation, inheritance, and polymorphism.