Lecture
The object-oriented approach is fundamentally different from the traditional approaches of structural design: here you need to think differently about the process of decomposition, and the architecture of the resulting software product is largely beyond the concepts of traditional structured programming. This is due to the fact that programs created using traditional approaches operate mainly with verbs (functions). We are trying to program the task as a sequence of functions and algorithms, having performed, for this, the so-called algorithmic decomposition. With the object approach, an object decomposition is carried out, and the program is already represented as a set of objects interacting with each other.
Object-oriented technology is based on the so-called object model. Its main principles are: abstraction, encapsulation, modularity, hierarchy, typification, concurrency, and persistence. Each of these principles is not new in itself, but for the first time in the object model, they are used in combination. The first four principles are obligatory in the sense that without them the model will not be considered objective. The last three are optional. Consider in detail all the principles.
Abstracting identifies the essential characteristics of an object that distinguishes it from all other types of objects and, thus, clearly defines its conceptual boundaries from the point of view of the observer. The phrase “from the point of view of the observer” is important, since different people may have completely different views on a thing or a problem.
Abstraction focuses on the external features of the object and allows you to separate the most essential behavioral features from non-essential ones. Abelson and Sussman called this separation of meaning and realization a barrier of abstraction, which is based on the principle of minimizing connections, when the object's interface contains only essential aspects of behavior and nothing more. There is another additional principle, called the principle of least surprise, according to which abstraction should cover the entire behavior of an object, but no more and no less, and not introduce surprises or side effects that lie outside its scope.
Choosing the right set of abstractions for a given subject area is the main task of object-oriented design. There are 4 types of abstractions (listed with decreasing utility).
Contract programming model
A very important concept for abstraction is the concept of a contractual programming model. We introduce several definitions.
A client is any object that uses the resources of another object (called a server). We will characterize the behavior of an object with the services it renders to other objects and the operations it performs on other objects. This approach focuses attention on the external manifestations of the object and leads to the idea that Meyer called the contract programming model: the external manifestation of the object is considered from the point of view of its contract with other objects, accordingly its internal structure (often in collaboration with other objects) objects). The contract fixes all the obligations that the server object has to the client object. In other words, this contract determines the responsibility of the object - the behavior for which it is responsible.
Each operation stipulated by this contract is uniquely determined by its formal parameters and the type of return value. The complete set of operations that a client can perform on another object, along with the correct order in which these operations are invoked, is called a protocol. The protocol reflects all the possible ways in which an object can act or be exposed, thereby fully defining the external.
The central idea in the contractual programming model is the notion of an invariant. An invariant is a certain logical condition, the value of which (true or false) must be preserved. For each operation of the object, you can specify preconditions (invariants assumed by the operation) and postconditions (invariants that the operation satisfies). Changing the invariant violates the contract associated with abstraction. In particular, if the precondition is violated, the client does not comply with its obligations and the server cannot perform its task correctly. If the post-condition is violated, then the server violated its obligations, and the client can no longer trust him. In case of violation of any condition, an exceptional situation is raised. Many programming languages have the means to work with exceptions: objects can raise exceptions in order to prohibit further processing and warn other objects about the problem, which in turn can take over the exception and deal with the problem.
Encapsulation is the process of separating from each other elements of abstraction that define its structure and behavior; encapsulation is used to isolate the contractual obligations of abstraction from their implementation.
Abstraction and encapsulation complement each other: abstraction is directed at the observed behavior of the object, and encapsulation is engaged in the internal device. Most often, encapsulation is performed by hiding information, that is, masking all the internal parts that do not affect the external behavior of the object. Usually, the internal structure of the object and the implementation of its methods are hidden. Encapsulation is necessary to ensure that the model that we express using the means of modeling and programming languages is adequate.
Consider an example of a plant. The plant has such an attribute as size. What can affect the size? The size can be affected by the amount of sunlight, the amount of water, the amount of manure finally. You influence the size by changing the amount of one or another of the listed parameters. If you water the flower it will grow. At the same time, you cannot force a flower to grow two meters in the real world by one effort of thought. This is unrealistic, since only the flower itself imagines the mechanisms by which its size changes. And it would be natural that in the program model of a flower it would not be possible to change the size of the flower clearly. Therefore, we hide the attribute - the size of the flower in the implementation, and in the model's interface, we set it to the method “Influence Growth” with parameters such as the amount of light, water and manure.
Information hiding is a relative concept: what is hidden on one level of abstraction is found on another level. You can get inside the objects; however, it is usually required that the developer of the server class take care of this specially, and the developers of the client classes are not too lazy to figure it out. Encapsulation does not save from stupidity; it, as noted Straustrup, "protects against errors, but not from cheating" Of course, the programming language has nothing to do with it; except that the operating system can restrict access to files that describe the implementation of classes. In practice, sometimes it is just necessary to familiarize yourself with the implementation of the class in order to understand its purpose, especially if there is no external documentation.
Polymorphism is a property of the system to use objects with the same interface without information about the type and internal structure of the object.
For example, if you are reading data from a file, then obviously, in a class that implements a file stream, there will be a method similar to the following: byte [] readBytes (int n);
Suppose now that you need to read the same data from the socket. The class that implements the socket will also have a readBytes method. It is enough to replace an object of one class with an object of another class in your system, and the result will be achieved.
In this case, the system logic can be implemented regardless of whether the data will be read from the file or received over the network. Thus, we abstract away from a specific data acquisition specialization and work at the interface level. The only requirement is that each object used has a readBytes method.
Polymorphism (multiformity) is a consequence of the idea of inheritance. In general terms, class polymorphism is a property of the base class to use the functions of derived classes, even if at the time of the definition it is not yet known which class will include it as the base class and thus become derived from it.
Consider the polymorphism property of classes based on the following example:
Use the following commands:
$ a-> Call (); // displays "Test from A" $ b-> Test (); // displays "Test from B" $ b-> Call (); // Attention! Displays "Test from B"!
Pay attention to the last line: contrary to expectations, it is not the Test () function from class A that is called, but a function from class B ! It seems that Test () from B simply redefined the Test () function from A. So it really is. A function that is redefined in a derived class is called virtual.
The mechanism of virtual functions allows, for example, to “slip” functions that are waiting for an object of one class, an object of another, derived, class. Another classic example is a class that embodies the properties of a geometric figure, and several classes derived from it - a square, a circle, a triangle, etc.
The base class has a virtual function Draw () , which causes the object to draw itself. All derived shape classes, of course, override this function (after all, each shape needs to be drawn in a special way). We also have an array of figures, and we don’t know which ones. But, using polymorphism, we can, without thinking, sort through all the elements of the array and call Draw () for each of them - the figure itself “decides” what type it is and how to draw it.
And here is another practical example showing the property of a class - polymorphism:
Base class function "; } function base_funct () { $ this-> funct (); } } class Derivative extends Base { function funct () { echo "
In the above example, the base_funct () function of the Base class was overwritten by the same function of the Derivative class.
Modularity is a property of a system that has been decomposed into internally connected, but weakly interconnected modules.
According to Myers, “Dividing a program into modules can reduce its complexity to a certain degree ... However, it’s much more important that a set of well-defined and documented interfaces are created inside a modular program. These interfaces are invaluable for a comprehensive understanding of the program as a whole.” In some programming languages, such as Smalltalk, there are no modules, and classes constitute the only physical basis for decomposition. In other languages, including Object Pascal, C ++, the Java module is an independent language construct. In these languages, classes and objects constitute the logical structure of the system, they are placed into modules that form the physical structure of the system. This property becomes especially useful when the system consists of many hundreds of classes.
In most languages that support the principle of modularity as a separate concept, the interface of the module is separated from its implementation. Thus, modularity and encapsulation go hand in hand. Modularity is supported differently in different programming languages. For example, in C ++, modules are separately compiled files. For C / C ++, it is traditional to place the interface part of the modules into separate files with the .h extension (the so-called header files). The implementation, that is, the text of the module, is stored in files with the .c extension (C ++ programs often use the cp and .cpp extensions). The connection between files is declared by the macro directive #include. Such an approach is based solely on agreement and is not a strict requirement of the language itself. In the Object Pascal language, the modularity principle is formalized somewhat more strictly. This language defines a specific syntax for the interface part and the implementation of the module (unit). In Java, there are so-called package. Each package contains several classes grouped according to some logical feature.
The modularity, in addition to facilitating the search for the desired description, significantly speeds up the project build process (of course, for compilers supporting separate compilation). Consider an example.
Naturally, all this imposes a very tough constraint on the stability of interfaces, but the task of forming stable interfaces is a design problem in general.
Abstraction is a useful thing, but always, except for the simplest situations, the number of abstractions in the system far exceeds our mental capabilities. Encapsulation allows to eliminate this obstacle to a certain extent by removing the internal content of abstractions from the field of view. Modularity also simplifies the task by combining logically related abstractions into groups. But this is not enough.
A significant simplification in the understanding of complex tasks is achieved through the formation of a hierarchical structure from abstractions. We define the hierarchy as follows:
Hierarchy is the ordering of abstractions, their arrangement in levels.
The main types of hierarchical structures in relation to complex systems are the structure of classes (hierarchy "is-a") and the structure of objects (hierarchy of "part of").
1.2.4.1 The is-a hierarchy
An important element of object-oriented systems and the main type of is-a hierarchy is the above-mentioned concept of inheritance. Inheritance means a relationship between classes (the parent / child relationship) when one class borrows the structural or functional part of one or more other classes (single and multiple inheritance, respectively). In other words, inheritance creates a hierarchy of abstractions in which subclasses inherit a structure from one or more superclasses. Often a subclass completes or rewrites the components of a higher class.
Semantically, inheritance describes an is-a relationship. For example, a bear is a mammal, a house is a real estate and "quick sorting" is a sorting algorithm. Thus, inheritance generates a “generalization-specialization” hierarchy, in which a subclass is a specialized special case of its superclass. "Litmus test" inheritance - checkback; so, if B is not A, then B should not be produced from A. In addition to the single inheritance, examples of which we have just cited, there is another variant of inheritance - the plural one. In this case, a certain class admits at the same time two generalizations, for example, an apple can be considered as a fruit and as a flower. The same can be said about cherries. Another example: a walkie-talkie is both a receiver and a transmitter.
Multiple inheritance is a simple thing, but it complicates the implementation of programming languages. There are two problems - name conflicts between different superclasses and re-inheritance. The first problem occurs when a field or operation with the same name is defined in two or more superclasses. In C ++, this kind of conflict must be explicitly resolved manually, and in Smalltalk, the one that occurs first is taken. The second problem is re-inheritance. Re-inheritance is when a class inherits two classes, and they separately inherit the same fourth. It turns out the rhombic structure of inheritance and it is necessary to decide whether the lowermost class should receive one or two separate copies of the uppermost class? In some languages, re-inheritance is prohibited, in others the conflict is resolved by a "volitional order", and in C ++ this is left to the discretion of the programmer.
Multiple inheritance is often abused. For example, cotton candy is a special case of sweetness, but not cotton. Применяйте ту же "лакмусовую бумажку": если B не есть A, то ему не стоит наследовать от A. Часто плохо сформированные структуры множественного наследования могут быть сведены к единственному суперклассу плюс агрегация других классов подклассом.
1.2.4.2 Иерархия "part of"
Если иерархия "is а" определяет отношение "обобщение/специализация", то отношение "part of" (часть-целое) вводит иерархию агрегации. Например человек-рука, огород-ростения.
Агрегация есть во всех языках, использующих структуры или записи, состоящие из разнотипных данных. Но в объектно-ориентированном программировании она обретает новую мощь: агрегация позволяет физически сгруппировать логически связанные структуры, а наследование с легкостью копирует эти общие группы в различные абстракции.
Понятие типа взято из теории абстрактных типов данных. Дойч определяет тип, как "точную характеристику свойств, включая структуру и поведение, относящуюся к некоторой совокупности объектов". Для наших целей достаточно считать, что термины тип и класс взаимозаменяемы. (На самом деле тип и класс не вполне одно и то же; в некоторых языках их различают. Например, ранние версии языка Trellis/Owl разрешали объекту иметь и класс, и тип. Даже в Smalltalk объекты классов SmallInteger, LargeNegativeInteger, LargePositiveInteger относятся к одному типу Integer, хотя и к разным классам . Большинству смертных различать типы и классы просто противно и бесполезно. Достаточно сказать, что класс реализует понятие типа).
Typification is a way of protecting oneself from using objects of one class instead of another (strong typing), or at least controlling such use (weak typing).
Typing forces us to express our abstractions so that the programming language used in the implementation supports compliance with the design decisions made. The idea of type matching is central to the concept of typing. For example, take physical units. By dividing the distance by time, we expect to get speed, not weight. There is no sense in multiplying temperature by force, but in multiplying the distance by force - it is. Finally, all of you in the first class were taught that you can not put apples and protein. These are all examples of strong typing, when an application domain imposes rules and restrictions on the use and combination of abstractions. With weak typing, things are a bit more complicated.
Weak typing is very closely related to the concept of polymorphism.
Polymorphism - the position of the theory of types, according to which names (for example, variables) can denote objects of different (but having a common parent) classes. Any object denoted by a polymorphic name can react in its own way to a certain common set of operations.
Consider an example.Let us have a graphics editor. The editor window is a container in which there are graphic objects. All graphic objects (triangle, rectangle, circle ...) are subclasses of the GraphicObject class. All this can be represented by a class diagram (Fig. 1.8)
Figure 1.8 Class hierarchy of the graphical editor
As can be seen from the diagram, the window of the graphic editor (class Window) contains (aggregates) graphic objects. All that the Window class knows about the GraphicObject classes is that the graphic object can redraw itself (you must call the paint () method for this). The Window class is not aware of the existence of the Circle, Rectangle, and Triangle classes. (This approach allows you to add new types of graphical objects to the editor without changing the implementation of the Window class. Next, when considering design patterns, we will look at similar techniques in more detail). So, when you need to redraw the editor window, you call the Window :: repaint () method. Its implementation may look something like this:
Window :: repaint ()
{
clearWindow ();
for (int i = 0; i
objects [i] -> paint ();
}
}
Таким образом, объект класса Window, просто вызывает метод paint() для каждого агрегируемого объекта не заботять о том, как именно этот метод будет работать. Этот пример полиморфизма демонстрирует так же суть слабой типизации. Действительно, реальные объекты, находящиеся в векторе objects – это указатели на объекты классов Circle, Rectangle и Triangle. Описаны же они как указатели на объекты класса GraphicObjects (см. первую часть определения полиморфизма). Кроме того, объекты из objects по разному реагируют на одноименную операцию repaint() (круг, квадрат и треугольник для своего отображения действительно выполняют разные действия) (см. вторую часть определения полиморфизма и определения слабой типизации (об управлении использования объектов одного типа, вместо объектов другого типа)).
There are tasks in which automated systems must handle many events at the same time. In other cases, the need for computing power exceeds the resources of a single processor. In each of these situations, it is natural to use several computers to solve a problem or use multitasking on a multiprocessor computer. A process (control flow) is a fundamental unit of action in a system. Each program has at least one control flow, a parallel system has many such flows: the age of some is short-lived, while others live during the entire session of the system. Real parallelism is achieved only on multiprocessor systems, and single-processor systems mimic parallelism due to time-sharing algorithms.
In addition to this "hardware" distinction, we will distinguish between "heavy" and "easy" parallelism in terms of the need for resources. "Heavy" processes are controlled by the operating system independently of others, and a separate protected address space is allocated for them. "Light" coexist in the same address space. "Heavy" processes communicate with each other through the operating system, which is usually slow and costly. Communication of “easy” processes is much easier, they often use the same data.
Many modern operating systems provide direct support for concurrency, and this fact has a very positive effect on the possibility of providing concurrency in object-oriented systems. For example, UNIX systems provide for the fork system call, which spawns a new process. Systems Windows 32 (NT, 2000, XP) - multi-threaded; in addition, they provide software interfaces for creating processes and manipulating them (see CreateProcess).
Вообще говоря, возможности проектирования параллельности в объектно-ориентированных языках не сильно отличаются от любых других, - на нижних уровнях абстракции параллелизм и OOP развиваются совершенно независимо. С OOP или без, все традиционные проблемы параллельного программирования сохраняются. Действительно, создавать большие программы и так непросто, а если они еще и параллельные, то надо думать о возможном простое одного из потоков, неполучении данных, взаимной блокировке и т.д.
К счастью, на верхних уровнях OOP упрощает параллельное программирование для так как объектная модель неявно разбивает программу на (1) распределенные единицы и (2) сообщающиеся субъекты.
В то время, как объектно-ориентированное программирование основано на абстракции, инкапсуляции и наследовании, параллелизм главное внимание уделяет абстрагированию и синхронизации процессов. Объект есть понятие, на котором эти две точки зрения сходятся: каждый объект (полученный из абстракции реального мира) может представлять собой отдельный поток управления (абстракцию процесса). Такой объект называется активным. Для систем, построенных на основе OOD, мир может быть представлен, как совокупность взаимодействующих объектов, часть из которых является активной и выступает в роли независимых вычислительных центров. На этой основе дадим следующее определение параллелизма: Параллелизм - это свойство, отличающее активные объекты от пассивных.
I would like to note that most modern programming languages in one degree or another support parallelism. Of the common languages, the most complete support for concurrency is present in Java and C #. In C ++ there is no parallelism per se, but it is achieved by using the appropriate libraries.
Any program object exists in memory and lives in time. Can. assume that there is a continuous set of the duration of the existence of objects: there are objects that are present only during the evaluation of the expression, but there are also such as databases that exist independently of the program. This range of object persistence covers:
Традиционно, первыми тремя уровнями занимаются языки программирования, а последними - базы данных. Этот конфликт культур приводит к неожиданным решениям: программисты разрабатывают специальные схемы для сохранения объектов в период между запусками программы, а конструкторы баз данных переиначивают свою технологию под короткоживущие объекты.
Унификация принципов параллелизма для объектов позволила создать параллельные языки программирования. Аналогичным образом, введение сохраняемости, как нормальной составной части объектного подхода приводит нас к объектно-ориентированным базам данных (OODB, object-oriented databases). На практике подобные базы данных строятся на основе проверенных временем моделей - последовательных, индексированных, иерархических, сетевых или реляционных, но программист может ввести абстракцию объектно-ориентированного интерфейса, через который запросы к базе данных и другие операции выполняются в терминах объектов, время жизни которых превосходит время жизни отдельной программы.
Programming languages, as a rule, do not support the concept of persistence; A notable exception is Smalltalk, which has protocols for storing objects on disk and booting from disk. However, writing objects to unstructured files is still a naive approach, suitable only for small systems. As a rule, persistence is achieved by using (a few) commercial OODBs. Another option is to create an object-oriented wrapper for relational databases; this is better, in particular, for those who have already invested in the relational system.
Persistence is not only a problem of saving data. In OODB, it makes sense to save classes as well, so that programs can correctly interpret the data. This creates great difficulties as data increases, especially if the class of an object suddenly needs to be changed.
In conclusion, we define persistence as follows:
Persistence - the ability of an object to exist in time, experiencing the process that generated it, and (or) in space, moving from its original address space.
As mentioned above, the object model is fundamentally different from models that are associated with more traditional methods of structural analysis, design and programming. This does not mean that the object model requires the abandonment of all previously discovered and time-tested methods and techniques. Rather, it introduces some new elements that are added to previous experience.
The object approach provides a number of essential amenities that other models did not provide. Most importantly, the object approach allows you to create systems that satisfy the five attributes of well-structured complex systems. According to our experience, there are five more advantages that the object model provides.
Comments
To leave a comment
Object oriented programming
Terms: Object oriented programming