⬆️ ⬇️

Impurities VS delegation: advantages and disadvantages in the implementation of "plug-ins"

In this article, I offer you my view on the choice of using impurities or delegation in projects to add new functionality to the class.



The initial conditions are as follows: we consider impurities that have their own state and have access to all members of the aggregator class. All public members of the impurity class become part of the aggregator. We leave behind the question of speed. We investigate the question by the example of adding a new functional to the model of an invented ORM.



Basically, this article is related to PHP, but with some reservations it is also suitable for many other dynamic languages, which allow implementing impurities in one way or another.

')





This article is in some way a logical continuation of this article on the implementation of impurities in PHP. If you're interested, read. If not, it will not be a problem for understanding the material presented here.



Code samples



First, consider the differences between the two approaches on the example of code that does the same thing.



Impurity



class Aggregator { } class Mixin { public function doSomething() {} } Mixins::mix("Aggregator", "Mixin"); $a = new Aggregator (); $a->doSomething(); 




Delegation



 Class Aggregator {} class Mixin { public function doSomething() {} } class AggregatorMixed extends Aggregator { private $mixin; function __construct() { $mixin = new Mixin(); } public function doSomething() { $mixin->doSomething(); } } $a = new AggregatorMixed (); $a->doSomething(); 




Note that in some cases you can avoid inheritance if you can include delegated methods directly in the Aggregator class. But this is not always possible and I gave an example here in this form due to the consideration of impurities and delegation in the context of the implementation of plug-ins to ORM, which you will find below.



Technical differences



What is the difference between the two fragments from a technical point of view? They provide the same functionality, as you can see.



1. The impurity methods are invoked dynamically by name, and in the case of delegation, the compiler can check before the code is executed.



Dynamic calls bring flexibility to the program, which comes at the cost of not typing at compile time. If you use impurities, additional unit tests will be useful to you.



2. In the case of an impurity to add functionality, we simply mix it into the same class at any time, and in the case of delegation, we need to create a subclass with new methods to add functionality that delegates the required functionality.



Creating and maintaining a subclass requires some effort, the amount of its own code increases slightly, but in the end you see a holistic class in which you can see all the opportunities it provides. Impurities are connected to the classes very quickly without any additional coding, but in the end you see the class-unit separately and all the impurities separately.



Another small consequence is that in the case of delegation, the aggregator class decides on the volume of delegated functionality, in the case of impurities, the impurity itself.



3. The impurity automatically has access to all members of the aggregator class and can arbitrarily manipulate it, provided that it knows how it works. In the case of delegation of this access is not.



Access to the members of the aggregator class of impurities gives us the opportunity to change the internal state of the object. This is convenient, but adds to the impurity the need for knowledge of the internal structure of the class-aggregator. On the other hand, when developing an interface for an aggregator class using impurities, we do not need to plan the level of visibility of the class members, as is necessary in the case of delegation, if the behavior of the aggregator class is manipulated when extending the behavior. However, the impurity is easy to take away this privilege.



Now let's see how the same features can be implemented with impurities and delegation.



Example "Behavior in ORM".



We made a small site on our fictional framework. This site has a directory of articles. Our framework contains ORM, in which there is a “article” model. Our ORM also includes classes that support common behaviors: wood, versioning, soft-delete, and so on.



Suppose that we need to make a directory of articles tree in the next version of our framework. What are we doing? We add the tree behavior model to the article class. We can choose either impurity or delegation in this case. The results will resemble those examples that we considered at the very beginning. Pay attention to one moment. In the case of the use of impurities, the visible interface of the main class has not changed at all, and in the case of delegation, it has only expanded, but still remained compatible.



In the next version of the framework, we need to add versioning and soft-removability to the model. What we easily do is either adding the appropriate impurities to the model, or expanding the model's class with the help of delegation.



Please note that in this example it is possible to use both impurities and delegation.



Now let's consider a similar example, only now we are not adding functionality to the model, but plug-ins to our framework, which are written by third-party developers. All that is required of us is to provide a convenient API so that to integrate the plug-in into the system we do not have to rewrite most of it.



The plugin wants to add functionality to our model. If the plugin is built on impurities, it simply registers itself for the class of the model. And in the case of delegation, we could, for example, store the model class name in the registry. Then the add-on tree plugin to articles inherits itself from the base class of the framework model, adds functionality and replaces the name of the original model class with its own in the model registry. Or the developer of the plug-in provides the user of the system with instructions on how to add the model class to the desired plug-in (as you understand, this is less preferable for ordinary users). The scheme is a bit complicated, but for now it is tolerable. And our framework will continue to work in both cases, since the interface of the class of the model remains compatible (attentive readers probably noticed that there are other solutions for expanding the functionality of the class of the model in our project. But their consideration will be the topic of a separate article).



But the authors of plug-ins of "versioning" and "soft-removability" want these features to be added to the model of articles not "instead of", but "together"! How can this be implemented? In the case of impurities, everything is still simple. We add new and new functional to the class of the model. And with delegation? Apparently, here the scheme starts to get complicated. We need the plug-in classes to inherit from each other, and the first plug-in class from the original model class. With certain costs, such functionality can also be added. What you prefer, you must decide for yourself.



In this case, the choice should be made based on your professional skills and preferences. In a serious, non-educational project, you should use only the technique that is most familiar and familiar to you professionally (or the one that is most familiar and familiar to most programmers who will work on the project in the future). If you ignore this recommendation and choose what you currently feel is just more convenient, make sure that using an unfamiliar approach does not become critical for the health of the project code. If you think you are equally good at using both impurities and delegation, the considerations for choosing both are given above. Decide what you like best.



When the control over the code leaves your hands, you will obviously have to go over to the impurities, or look for other ways to solve the problem, since it is obvious that the solution given using delegation will be complicated.



I regret that I may have disappointed lovers of techno-srachs, who, most likely, expected to see me among the fierce supporters of one of the approaches that protect their native bastion. I'm sorry if this is the case.



Remember that any choice in life should always be made, guided by two things: the maximum amount of information you can gather on the subject and your own head. I tried to provide the first. The second is yours.



PS If this article seemed interesting to you, suggest in the comments a topic for the next one.

Source: https://habr.com/ru/post/108097/



All Articles