The correct use of the “Bridge” pattern (Two-way Bridge) or MVC -> “Business Entity - Visualization - Controller”

    Background

    Article The misuse of the Bridge / Bridge design pattern, as it happened, divided the audience into two. Then I thought, saying A did not say B, it would not be right. No, I don’t refuse my words, but I found where and how I used the “Bridge” pattern. Because it is also misunderstood, it seems the alternative name "Descriptor / body" is less misleading.

    So where is it? It turned out in my analogue of using the concept of MVC (Model / View / Controller).

    Therefore, at first I will introduce my variation “Business Entity - Visualization - Controller”. I already wrote it, but I think few people are familiar with this. And then we'll see where is the “Right Bridge”.

    PSI was given a credit of trust here, and I undertook to write another article on improving the Flyweight pattern - I wrote a report .



    Separation of visualization and business logic

    The model-view-controller is the most well-known principle of software architecture, in which the application data model, user interface and control logic are divided into three separate components, so that modifying one of the components has minimal impact on other components. The description and some aspects, now historical in nature, are described in the article by Sergey Rogachev, “Generalized Model-View-Controller”, 2007. Another series of examples are in Ivan Bodyagin, Model-View-Controller in .Net and Andrey Ozerov, MVC Triad In action.

    In reality, the use of this model is fraught with a number of problems and applications built on this model, despite the declaration, are not flexible and have little connection. The very idea of ​​separating visualization from business logic is declared in it, but the connections between the model, presentation and controller are completely ineffective.

    Note that not every “Business Entity” has a visualization, but as a rule, the same “Business Entity” can, depending on the context, be used both with visualization and without visualization. However, it should not lose its functionality because of this. Note that if the "Business Entity" is used without visualization, then creating presentation and controller objects is completely unnecessary.

    But sometimes a model (the so-called passive model) is really understood only as data (properties, methods for accessing them, getting them from the database) without business logic, then when you separate it, such a model becomes useless by itself. Let us verify the rationality of the classical passive model “Model-view-controller”, in most cases, if you do not create presentation and controller objects, the remaining model object will remain completely “paralyzed”, that is, it will remain non-functional. Thus, we see that the declaration of independence in this model is a fiction.

    But later, ideas about the active model were developed, when the model really means a business entity, as a combination of data and business logic. Then everything is in order, but you need to be extremely careful so that the business logic is not left in the visualization classes or the controller.

    Therefore, to begin with, the separation of data from the actual methods (business logic) is unacceptable in an object-oriented approach, and as we see, if this is not done, the model will not be functional (“paralyzed”). Therefore, in order not to get confused, we need to clarify at least the terminology and we will talk about the architectural principle called “Business Entity - Visualization - Controller” instead of “Model-view-controller”. So far, we have changed only the words (then we will see that this is quite fundamental) and refused to consider the passive MVC model as not sound.

    Now let's look at the declaration for the Model-View-Controller model:

    While the view and controller depend on the model, the model does not depend on the view or the controller. This is a key feature of the separation, which allows you to work with the model, and therefore with the business logic of the application, regardless of the visual representation.

    But besides the independence of the model from the visual representation, the opposite is often necessary. You must also have the independence of visual representation from the model. And this is impossible by definition in MVC.

    So the first one. The words presentation depends on the model can mean one of two
    things : visualization creates (generates) a model (business entity), and from the point of view of classes, visualization aggregates a business entity
    (or softer version) the controller creates both the model (business entity) and the visualization. And then there are two options for how the controller will dispose of links to these objects:
    visualization receives a link to both the controller and the model, i.e. again, visualization aggregates the business entity;
    visualization receives a link only to the controller, but the controller contains delegate methods that are directly addressed to the model, i.e. formally, visualization does not aggregate a business entity, but only a controller, but indirectly (since the controller in turn aggregates the model) again, this leads to aggregation of the business entity

    Doesn't that seem strange to you at least? It turns out that a business entity cannot decide how to visualize itself, or whether to visualize it at all. Moreover, the user (in this case, the application developer) must create a visualization object or controller, and in some strange way must work through a visualization or controller with a business entity. Although acceptable visualization may not be required at all.

    Therefore, the first thing we recognize is the wrong decision. Visualization, despite being a rather specific action, is no more no less than just one of the functions of a business entity. And therefore, it is the business entity that must create the visualization object, aggregate it and decide when and with what object to visualize itself, and whether to do it at all. But the visualization object just does not need to know anything about the business entity and the controller, with the exception of those data fields that the business entity wants to visualize using the interface.

    But we still need to fulfill the fundamental condition:

    Visualization classes contain only the logic that is needed to work with the user interface. And classes of a business entity do not contain a visualization code, but they contain all the business logic.

    So we need to separate the code that processes the user interface from the code that processes the business logic. While the behavior (methods) is relatively easy to divide into parts, which cannot be done with data (fields, properties). Data should be in graphic elements and have the same meaning as data in the domain model (business entity). Therefore, the fields containing the data must be duplicated and their synchronization must be ensured (for example, the value in the TextBox graphic element must be synchronized with the value of the field of the business entity). Such synchronization requires two-way communication between visualization objects and a business entity. Therefore, in relation to data fields, it is fundamentally impossible to separate these two objects. But firstly, it can be limited only to data fields,

    C # uses the latest WPF technology for visualization, which provides a binding mechanism. Therefore, it remains only to indicate exactly which properties will be interconnected. And the dilemma arises - in what object should this be done? If this is written in the business entity, then we violate the rule classes of the business entity do not contain the visualization code, if this is written in the visualization class, then we break the rule visualization classes contain only the logic that is needed to work with the user interface. In the model-view-controller model, however, this is often specified in the visualization class.

    But we will act differently. One of the reasons for introducing a third, seemingly completely redundant class - the controller, is precisely what is indicated above. But first, let's try to clarify what a controller is by itself, and when it is created. The controller in the model "Business Entity - Visualization - Controller" is essentially a protocol of interaction between a business entity and its visualization. At the same time, when a business entity determines that it needs to be visualized somehow, it selects and creates an object of a certain visualization class, then creates one or another interaction protocol - a controller. At the same time, when creating the controller, she passes him two links to herself and to the selected visualization. After that, the business entity theoretically (it can be practically nonetheless difficult to achieve this completely) should no longer work with the visualization object,

    It turns out that the controller prescribes binding during initialization, linking the visual fields with the fields of the business entity, after which formally these two visualization objects and business entities are synchronized in accordance with the selected protocol, but there is no other interaction between them. This approach is called transparent communication, that is, there is no direct interaction, but only interaction occurs according to the protocol - the code that is registered in the controller.

    If you use a different programming language, and there is no way to use binding, you can replace it with a refactoring technique called Duplicate Observed Data [1].

    But you can implement binding yourself, it’s not very difficult. First, you must recognize that data synchronization between the model and the visualization is a low-level function that should be performed by a separate MySynh class. How to write it? The controller has a link to both the model and the visualization. He passes these links to MySynh. In addition, purely in a declarative form prescribes relationships of the type Visualization Field A = Model Property B, purely in text. The access methods get (), set () are written for each property in the model and visualization - in which, in addition to actually assigning a new value, an event of the form Changed FieldA is generated. This is necessary not only for synchronization, but also for calling logic to change the value. Therefore, it is in any case. What remains is, when MySynh is initialized, it subscribes to all events, which synchronization he was told takes care of. When a real event of a change in value occurs, о receives a call, looking in his database about which connections to support, finds the right one. And using the "reflection" (get a link to a property by its text name) assigns a new value.

    As a result, the data in the model and visualization are always synchronized, always the same. And neither one nor the other has links to each other. Those. they are completely independent and do not know about each other's existence.

    Now the second. In addition to transparent data synchronization, it is necessary to ensure the separation of behavior (methods). There is essentially a different logic - the visualization logic (for example, by clicking on the button, one or another field may become visible or visible), and business logic (for example, certain values ​​are calculated based on the entered data). It is necessary to provide visualization logic in the visualization class, and business logic, respectively, in the business entity. This is not always trivial, but substantial attention should be paid to this. The difficulty is that the signal from the user is the only one - for example, pressing a button, and you need to call two or more independent methods. At the same time, we cannot call methods of the business entity from the visualization object so as not to violate our basic principles of separation.

    And here again you need to apply the controller. But if we used the binding'a features for the data, then in this case we need to use the event technique. Then again, upon initialization, the controller associates the signals from the visual interface with the methods of the business entity, and again, instead of direct strict communication, we get a transparent connection.

    Therefore, we need a controller as a link between a business entity and visualization. And in this case, we really have almost completely independent classes of business entities and visualizations, and choosing the appropriate controller we can use various business entities and visualizations in any combination. Moreover, in comparison with the classical model-view-controller scheme, we can not only have different visualizations for a business entity, but also display different business entities with a certain visualization. Not to mention the problems identified above. With such an organization, we get a true separation of visualization from business logic in both directions. And also the business entity is still capable of making a decision on visualization itself simply by applying one or another controller to communicate with one or another visualization.

    This text may be difficult to understand. There is one good person who wrote with my assistance, perhaps a more understandable description of my idea. With code examples.

    The controller is the bridge

    If you look here for the "bridge", it turns out that the controller has two links to two "bodies". One on “Business Entity”, the second on “Visualization”. Why did he end up here if, as I argued in the first article, “separation into abstraction and implementation” is harmful.

    And all because the controller is not quite an implementation, and not quite an abstraction. Abstraction and implementation here are together as it should be in a “business entity”. But a certain logic of communication between visualization and business logic is taken out in the controller. This logic of communication is what is good to bring to the bridge. In essence, visualization is part of some implementation of the “business entity”, but it is very special. And when we separate it, it is very good to develop the “User Interface” and “Business Logic” separately in different hierarchies. But connect them through the controller. This is the correct use of the Bridge pattern. But it’s like a “Bridge with two-way traffic”, on the one hand we’re running towards business logic (solid), and on the other, towards “visualization”

    Also popular now: