The question of architecture, but quite often I come across this, and rarely see a good solution.

Suppose we have a certain interface:

CoffeeMachine { doCoffee(); } 

And we have a whole bunch of implementations, LateCoffeeMachine, CapuchinoCoffeeMachine, etc. And everything is going well, only we suddenly need to output the machine parameters somewhere.

 view(CoffeeMachine machine); 

But for each its own parameters, one operates only the amount of coffee, the other can also sugar, and the third also milk.

It seems to be the easiest and most logical option to change the interface of CoffeeMachine:

 CoffeeMachine { doCoffee(); view(); } 

That's just this approach violates the principle of Single Responsibility, because the machine is now responsible not only for making coffee, but also for displaying itself, so you can add methods to infinity.

The second option that personally comes to me in the head is a visitor pattern. Which in my opinion is quite cumbersome.

The third option that comes to my mind is through the factory, to create objects in pairs. However, this method leads to the appearance of Generic (although it is possible without them, but type safety) and, again, some problems.

 Viewer<T> { view(T); } Factory { createMachine() createViewer(); } 

In general, I myself can offer a few more solutions, but I would be interested to look at someone else's opinion and thoughts of others on this matter. Maybe I'm wildly stupid and have an elegant solution or a switch'a can not be avoided? )

    1 answer 1

    Based on my experience with OOP, I would make one of two decisions:

    1. If the view behavior is specific only to CoffeeMachine, then I see nothing bad to include an additional method in the interface. It will just be very correct to indicate that each CoffeeMachine has an idea, in addition to the fact that it can also make coffee.
    2. If the view behavior is not specific to CoffeeMachine, but may be inherent to other elements of the system, then I would highlight the additional IViewable interface with the view method. Well and, accordingly, indicated that the appropriate classes for making coffee implement not only the CoffeeMachine interface, but also the IViewable interface. That will allow to transfer references to objects of these classes to classes handlers for displaying views. For other elements that also need to be processed for presentation, you also need to specify that they implement the IViewable interface, but without CoffeeMachine, which does not contradict the logic of working with objects.

    The second solution is only suitable if multiple implementations are allowed (I think that now this is almost in all systems). And all sorts of factories there are too much complication. IMHO.

    • The 2nd option in my opinion is better - it fulfills the rule about small interfaces from SOLID. But there is simply no problem, when the thought arises, “should I not add the 2nd or 3rd method (or interface) to the class?”, But when such methods accumulate under 10–20 and they are already very indirectly related to each other, you start slowly think about architecture) By the way, the factories are very spontaneous. On the one hand, we get a cleaner architecture and more flexibility, on the other hand, you are right - the system is complicated by a bunch of sometimes unnecessary abstractions. - Uraty
    • According to design experience, my interfaces rarely contain more than two methods. I do not know why. Maybe this is due to the tasks that I need to solve. This is mainly the integration of several processes with the transfer of different data to each other. Therefore, it is wrong to fence one large interface, since many methods are simply hung in stubs during implementation. But many small interfaces allow flexible management of exchange protocols. Large interfaces are needed for libraries IMHO. When you need to declare a set of unrelated methods. - Andrey Tumanov