2 Patterns of designing classes and objects 2.1 Mechanisms of reuse

Lecture



The two most common ways to reuse functionality in OO systems are class inheritance and object composition. As already mentioned, class inheritance allows you to define an implementation of one class in terms of another. Reuse by spawning a subclass is often called a “white box reuse”. Such a term emphasizes that the internal structure of the parent classes is visible to subclasses.

The second way to reuse some functionality is the composition of objects. In this case, we obtain a new, more complex functionality by installing relationship relations or aggregation between objects. Composition requires that objects have well-defined interfaces. This method of reuse is called the “black box” (black-box reuse).

Both inheritance and composition have advantages and disadvantages. Inheritance is determined statically, at compile time, it is easier to use because it is directly supported by programming languages. Because of this, inheritance works quickly and is written quickly. On the other hand, during inheritance, you cannot replace the implementation inherited from the parent during the execution of the program. In addition, the parent class often at least partially determines the physical representation of their subclasses and it often happens that a change in the parent leads to a cascade change of all, or most, of the subclasses. Finally, when inheriting, subclasses have access to the parent fields, which violates the principle of encapsulation. These deficiencies of inheritance, especially the main one - the dependence of subclasses on the implementation of the superclass, can lead to problems when trying to reuse classes. If at least one aspect of the class turned out to be unsuitable for a new business environment, you have to rewrite the parent class, or even program a new inheritance hierarchy. You can cope with the problem if you only inherit abstract classes, because they usually have no implementation or it is minimal.

The composition of objects is determined dynamically at run time due to the fact that objects use references to other objects. Composition can be applied if objects comply with each other’s interfaces. This, in turn, requires carefully designing the interfaces so that one object can be used with a wide range of others. But the win is great. Since access to objects is carried out through their interfaces, we do not break encapsulation. During the execution of the program, any object can be replaced by another, so long as it has the same interface.

Designing OO programs is difficult in itself, and if they need to be reused, then everything becomes even more complicated. It is necessary to select suitable objects, relate them to different classes, observing a reasonable degree of detail, determine the interfaces of classes and the inheritance hierarchy, and establish essential relationships between the classes. The design must, on the one hand, correspond to the problem to be solved, on the other - to be general, in order to manage to take into account all the requirements that may arise in the future. I would like to avoid at all or, at least, minimize the need for redesign. The developers who are accustomed to the OO design will tell you that it is very difficult, if possible at all, to provide the “right”, that is, a residually flexible and reusable design. Before considering the goal as achieved, they usually try to test the solution found on several tasks, each time modifying it. To create the best design “on the knees”, that is, immediately and without special efforts, only brilliant people or a real pro succeed. On the other hand, newcomers who try to apply the techniques of OO design often experience shock from the number of possible options and return to the usual OO methods. It takes a lot of time before it comes to understanding what a successful OO design is. Experienced designers obviously know some subtleties that elude beginners. We will try to answer the question: “What are these subtleties?”.

First of all, an experienced developer understands that you do not need to solve every problem from scratch. Instead, he is trying to re-use solutions that have been successful in the past. Having found a good solution once, he will resort to it again and again. Often, thanks to the experience gained, the designer becomes an expert. Naturally, invaluable experience is obtained not only by its cones. Fortunately, many OOD gurus share their secrets. Their solutions are accumulated, systematized and used by designers all over the world to ensure that their designs are flexible, modifiable and expandable. Such solutions are called design patterns.

According to Christopher Alexander, “any pattern describes a task that arises again and again in our work, as well as the principle of its solution, and in such a way that this solution can be used a million times without reinventing anything.” And although Alexander meant arising from the design of buildings and cities, his words are true in relation to OOD patterns. Our solutions are expressed in terms of objects and interfaces, not walls, doors and window frames, but in both cases the meaning of the pattern is to offer a solution to a specific task in a specific context.

In general, the pattern consists of four main elements:

  1. Name. Referring to it, we can immediately describe the problem of design, its solutions and their consequences. Naming patterns allows you to design at a higher level of abstraction. Using pattern names, you can communicate with colleagues, for example, I can say: “To create objects in this case, I suggest using the factory method”. Or, if you allow yourself some style freedom, "Here we use the principle of Hollywood." In short, the assignment of name patterns simplifies communication in a professional environment.
  2. Task. A task is a description of when to apply a pattern. It is necessary to formulate the task and its context. A specific design problem may be described, for example, a method for representing algorithms as objects. The same task may include a list of conditions under which it makes sense to apply this pattern.
  3. Decision. The solution is a description of the design elements, the relations between them, the functions of each element. The specific design or implementation is not meant, since a pattern is a pattern that is applicable in a variety of situations. It simply gives an abstract description of the design problem and how it can be solved with the help of some very generalized combination of elements (in our case, the elements are classes and objects).
  4. Results. The results are consequences of the use of the pattern and various kinds of compromises. Although when describing design decisions, the consequences are often not mentioned, it is necessary to be aware of them so that you can choose between different options and evaluate the advantages and disadvantages of this pattern.

In some cases, when describing patterns, additional elements are specified. Let's talk about them when considering specific patterns. Now I would like to talk a little about how design problems are solved with the help of patterns.

created: 2014-10-05
updated: 2021-03-13
132548



Rating 9 of 10. count vote: 2
Are you satisfied?:



Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Object oriented programming

Terms: Object oriented programming