I solve the problem of finding a leader ( leader election ).

The task is purely algorithmic. There are 2 forms of task. I have an abstract class for representing data and an abstract class Solver. For each form of the task I extend these classes in accordance with the needs of this form of task. That is, solving the problem for the first form, I need to write like this in the client code:

MyData data = new MyDataForm1(); MySolver solver = new MySolverForm1(); 

This could be compiled like this:

 public abstract class AbstractTask{ // some code } public class Task1{ MyData data = new MyDataForm1(); MySolver solver = new MySolverForm1(); //some code } public class Task2{ MyData data = new MyDataForm2(); MySolver solver = new MySolverForm2(); //some code } 

Then in the client code for the first form, for example, you can do this:

 AbstractTask task = new Task1(); 

Is this a composition? But there is such a problem: It turns out that Task 2 classes have responsibilities. They store the data and solve the problem (yes, delegating it to MyData and MySolver instances, but still). And so it always turns out with composition. We include instances of several classes in one class as fields. The included classes had some responsibilities. So the inclusive class will have several responsibilities.

I understand here that this is a higher level of abstraction. For example, at the level of individual parts of the machine, you can consider the motor, radio and steering wheel as separate classes that perform some duties. But then we start to consider the car. It consists of these parts. But now we are considering a higher level of abstraction (we just have to drive), so we can say that the class Machine still performs only one task (driving).

  • Completed his answer by example. - Pavel Krizhanovskiy
  • one
    You yourself answered your question in the last paragraph :). - andreycha

3 answers 3

The included classes had some responsibilities. So the inclusive class will have several responsibilities.

Nah Only his own. In any case, it will look like from the outside .

Encapsulation is valid . The obligation of the nested class is already an internal part of the implementation and this part should not be visible to those who use this class.

This also implies a rule by which it can be understood whether the use of the composition in each specific case is reasonable: whether the “internal” obligation is completely “immersed” in the “external” (without significant additional “stretching”).

    I understand here that this is a higher level of abstraction. For example, at the level of individual parts of the machine, you can consider the motor, radio and steering wheel as separate classes that perform some duties. But then we start to consider the car. It consists of these parts. But now we are considering a higher level of abstraction (we just have to drive), so we can say that the class Machine still performs only one task (driving).

    You yourself here correctly answered the question. The fact is that the new class will have a duty that is different from the duties of the objects it includes. Specifically, the responsibility of the new class is to bring the data and the solution algorithm together. In the future it may be found that such an object will be very useful if intermediate steps are needed. This duty sounds somewhat abstract, but nonetheless has the right to life. And, as you can see, it is higher-level than the duties of the included classes. I casually mentioned this in the previous answer to your question about SRP.

    You can deduce the rule: the higher the level of the “module”, the more high-level are its responsibilities.

    Take the type float . His duty is to implement work with floating point numbers (representation, plus basic mathematical operations).

    Using the float type (i.e. using the composition), you write the function for calculating the hypotenuse for the given cathets. The duty of this function is to consider the hypotenuse.

    Next, you include this function in the PlanimetryAlgorithms package. The duty of this package is to provide various algorithms related to planimetry (that is, the geometry of the shapes on the plane ).

    The PlanimetryAlgorithms package can, in turn, be included in the Geometry library, which will also include packages for other sections of the geometry. The duty of this library is to provide various functions relating to geometry in general .

    As can be seen, with increasing granularity, the duty becomes higher, but its uniqueness is respected . The function of calculating the hypotenuse does not give us at the same time the values ​​of all angles in this triangle, the PlanimetryAlgorithms package does not contain methods for drawing figures, and the Geometry library does not suddenly begin to engage in physics.

      True, the whole thing is in the level of abstraction. Objects that perform a single function of the highest level, to perform their duties, use the functions of the included objects. The duty of the electric motor as a separate object is to convert electrical energy into mechanical energy (rotation of the shaft). The object, for example, emery performs its function - provides a tool for sharpening knives. A trolley bus is another function. Both of them include an electric motor in one form or another (heirs of an abstract electric motor), and use it to perform their function.

      We also have objects like "Swiss knife": an object like one, but there are a lot of functions. With this you need to be careful when designing, so as not to include "extraneous" functions and not violate the principle. Also a good example of composition.