Study notes of PureMVC framework

I used to use a simple UI framework for the UI layer of my own projects. I found that the scalability was not strong and multi-person collaboration in large projects was also a problem, so I learned the more classic MVC-based PureMVC and the MVVM-based Loxodon Framework. Today I will record some information about PureMVC. study.

MVC thought

What is MVC?

The full name of MVC is Model View Controller, which is the abbreviation of model-view-controller. It is a software design model. It uses a method to separate business logic, data and interface display to organize code and gather numerous business logics into one component. While the interface and user interaction need to be improved and personalized, there is no need to rewrite the business logic. , to reduce the encoding time.

MVC began to exist in desktop programs. M refers to the business model, V refers to the user interface, and C refers to the controller.

The purpose of using MVC is to separate the implementation codes of M and V, so that the same program can use different forms of expression. The Model and View layers are decoupled through the Controller. Similarly, MVVM and MVP also decouple the Model and View layers through VM and P. The advantage of adopting this idea is that it reduces the coupling between M and V, facilitates expansion, reuse and management.

clip_image006

Figure: MVC structure diagram (solid line -> represents dependencies; dashed line -> represents events, notifications, etc.)

The core ideas of MVC are: code reusability and separation of concerns.

Model: The data model is used to encapsulate data related to the business logic of the application and how to process the data. Model has direct access to data, such as access to a database. ""Model"" does not depend on "VIew" and "Controller". The data in the model is generally released through a refresh mechanism. In order to implement this mechanism, those Views that need to monitor this Model must first register on this Model. Achieve monitoring changes in the data model. (Observer pattern "publish and subscribe")

View (View): The view layer can realize the purposeful display of data (optional function). There is generally no program logic in View (you can have a simple display interface). In order to implement the refresh function on the View, the View needs to access the data model (Model) it monitors, so it should first register with the data monitored by the View.

Controller: The controller plays an organizational role at different levels and is used to control the flow of the application. It handles events and responds. "Events" include user behavior and changes in the data model.

Simple UI framework

1. Core methods of UI framework

  • BaseUI will record the name of the UI and the Controller is used to decouple the UI from specific operation methods.
  • UIManager uses a dictionary to record all UI components, and then provides some methods to open and close the UI.
  • EventManager (message system) realizes the interaction between different modules. The message system generally uses the observer mode to register (subscribe) multiple methods with a keyword, and then uses this keyword to notify the registered methods.

2. UI framework expansion method

  • ResourceManager (resource manager) encapsulates some methods of loading Resources resources, such as synchronous loading, asynchronous loading, loading and instantiation, etc.
  • DataHelper (data parser) further encapsulates the serialization and deserialization plug-ins for XML, Json, Protobuf and other data formats.
  • Singleton singleton pattern
  • Common Extension (public extension component) uses generics and extension methods to further encapsulate methods in Unity to speed up development.

3. Advantages and disadvantages of simple UI framework

  • Easy to use
  • Relatively easy to understand for C# novices
  • Only suitable for very small projects
  • Very poor scalability
  • Logic will be piled up under the MonoBehaviour script, reducing efficiency.

After going through the pitfalls of the simple UI framework above, I realized that a larger project, or a collaborative and open project with multiple people, must have a more reliable framework as the foundation. There are several common frameworks in Unity. , which is also an extension of the classic framework. Such as PureMVC, LoxodonFramework, etc.

PureMVC framework

PureMVC advantages and disadvantages:

  1. Use intermediaries, agents, and command patterns to achieve decoupling, and use Model, View, and Controller direct coupling to reduce it more thoroughly and improve the reusability of some codes.
  2. Both the View interface and Model data can be reused
  3. The code is too redundant. For simple functions, it is necessary to create corresponding View, Mediator, Command, Facade, Proxy, Model and other scripts.
  4. The operation process is relatively cumbersome, and the code in Mediator seems to be complicated and difficult to understand.
  5. Messages are more performance-intensive in the form of reflection (the new version already uses delegation)

PureMVC features:

  1. The delivery of notifications requires boxing and unboxing operations (the new version has adopted generic delegation)
  2. Commands/notifications are implemented in the observer mode, using reflection to obtain methods and execute them (the new version has been modified to delegate)
  3. There is no Service module that needs to be added by yourself
  4. And data is passed through notifications. SendNotification has only one object type parameter. It will feel like data transmission. First, the data can be combined into a class to pass, and only the class needs to be constrained.

PureMVC Core Scripts //Core files

1.View.cs : IView.cs

  1. The dictionary mediatorMap records registered mediators (key is the mediator name, value is the mediator interface)
  2. The observerMap dictionary records registered observers (key is the event name, value is the observer interface)
  3. Provide methods to register/unregister mediators and observers (Register, Remove)
  4. The core method notifies the observer method NotifyObervers
  5. Implement View singleton

The IView.cs interface specifies the methods to be implemented by View
2.Controller.cs: IController.cs

  1. The dictionary commandMap records registered commands (key is the name of the command, value is Func<ICommand>)
  2. Record the IView interface to execute notifications in the View. IView is assigned a value when constructing the Controller.
  3. Implement Controller singleton
  4. Provides interfaces for registration, cancellation, and command execution (Register, Remove, Excute)
  5. The IController.cs interface specifies the methods to be implemented by View

3.Model.cs :IModel.cs

  1. The dictionary proxyMap records the registered Model proxy (Proxy) (key is the proxy name, value is the proxy type interface)
  2. Methods to implement IModel, including registration, cancellation, and proxy acquisition methods
  3. Implement Model singleton
  4. The IModel.cs interface specifies the methods to be implemented by Model

GameObject: Basically the lowest layer (UI view), it does not rely on any PureMVC class except Unity's own components.

Mediator: only relies on GameObject and is bound one-to-one with GameObject. The input of GameObject is passed to the Mediator call through events .

Command: The lowest layer communicates with Mediator through the message system and relies on Proxy (because sometimes it is necessary to obtain entities through Proxy to perform some operations, and Proxy only performs some data operations).

Proxy: The lowest layer, does not depend on any class except the corresponding data class.

View: Singleton, in addition to embedding a message system in the observer mode, its responsibility is only to manage Mediator (Register, Remove, Retrieve, Has)

Controller: Singleton, which internally calls the listening method of the message system. Its responsibility is only to manage Command (Register, Remove, Execute, Has)

Model: Singleton, responsible only for managing Proxy (Register, Remove, Retrieve, Has)

Facade: Singleton, responsible for managing View, Controller, and Model. Because View has a message system, it also has the function of sending messages indirectly.

Notifier: Functional class, which calls Facade’s message system to notify other objects, so it is essentially calling View’s message system (Observer) to send messages.

It can be seen from the functions of these classes that each class has a single responsibility and the functions are clearly separated. The Controller uses the corresponding Type to create objects to call methods, and instantiates a class every time a command is executed. The Observer message system uses the method name of the reflection object (GetMethod) to call the method of the observation object. The new version has used generic delegation to solve this problem.

                                Figure: PureMVC design diagram (taken from the official website )

It can be clearly seen from the design drawing that in addition to the core layer Facade, there are also Mediator, Proxy, and Command.

The goal of the PureMVC framework is very clear. It divides the program into three low-coupling layers: Model, View, and Controller. It is managed uniformly by Facade.

  1. Model saves a reference to the Proxy object. Proxy is responsible for operating the data model and communicating with remote services to access data.
  2. View holds a reference to the Mediator object. The specific view component "ViewComponent" is operated by the Mediator object
  3. Controller saves the mapping of all Commands. Command can obtain the Proxy object and interact with it, and execute other Commands by sending Notifacation. (The essence is still the observation system inside View)
  4. Proxy is responsible for operating the data model. What is a data model? The data model is database, XML, etc. We can intuitively understand that Proxy is a class used to query, insert, update, delete and other operations on the data model. After the operation is completed, a Notification message will be sent to notify other layers to update. Proxy only sends information and does not monitor information to avoid coupling.
  5. Mediator is responsible for operating specific view components, including: adding event listeners, sending or accepting Notifications, and directly changing the status of view components. For example: add an event to the Button, send Notifacation when the Button is clicked, and tell the Controller to perform the corresponding operation. For example, for a login button, the Mediator will send a notification to the controller and tell it to perform the login operation. Mediator is also responsible for changing the state of the view directly. For example, after clicking the login button, Mediator changes the login button to gray to avoid repeated operations. It's also possible to display a message on the view telling me that a login operation is in progress etc. Summary Medoator is used to manage specific views.
  6. Command can obtain the Proxy object and interact with it, and execute other Commands by sending Notification. For example, after clicking the login button, the Mediator will tell the Controller to execute the corresponding Command. Since logging in requires knowing the user's information, the Command will tell the Proxy to query the database or other data models to access the corresponding data. After the Proxy queries the data, it sends a Notification to notify the Command that the query has been completed, and then returns the information to the Command for verification. At the same time, the Mediator can also receive the Notifacation sent by the Proxy and tell the user that the information is being verified through the view. After Command verifies the user information, it sends a Notification to return the verification result to the Mediator and informs the user of the verification result. Or the Command can also send Notifacation to perform other Command operations, such as verifying the reading of user information details, etc.

PureMVC design pattern

Proxy pattern: Provides a proxy for other objects to control access to this object.

Usage: The proxy mode in PureMVC isolates the direct interaction between data and other systems, and operates through the proxy class of the data. Because the proxy class inherits from Notifier, the Proxy can also access the View through Notifier.facade. and Controller, which effectively isolates the coupling between data classes and other classes, improving the reusability of data classes. The data in such data classes can be generated by planning tools, making development more convenient.

Appearance mode: Provides a consistent interface for a set of interfaces of a subsystem. This mode defines a high-level interface to make the subsystem easier to use, perfectly embodying dependency inversion and Demeter's law. Using the appearance pattern can reduce the dependencies between subsystems. You can set up a Facade class for the old subsystem, and then use the Facade class to interact with the new system, reducing the old system's dependence on the information system and creating complex relationships.

Usage: The Facade class written using appearance mode in PureMVC is the main external interface of the entire MVC framework. View, Model, Controller are recorded in the Facade class and singletons are implemented. It has almost all external interfaces in the MVC core class. It is a typical high-level interface.

Observer pattern: It is a one-to-many dependency relationship that allows multiple observer objects to monitor a theme object at the same time. When the theme object changes, all observer objects will be notified so that they can automatically update themselves.

Usage: In the PureMVC framework, use the observer mode and register the corresponding UI event for the mediator in the constructor of the mediator class. When the UI triggers the event, the mediator will send the notification to the observer through the View. In the View Record the dictionary of observers corresponding to each event, traverse the observers and then execute the specific events in the notification. Notification events are added by overriding the ListNotificationInterests method when constructing the mediator. The command mode implementation in PureMVC also sends notifications and allows observers to execute commands triggered by calling the ExcuteCommand in the Controller.

 Mediator pattern: Use a mediator object to encapsulate a series of object interactions. Mediators eliminate the need for explicit references between objects, making them loosely coupled and allowing them to independently change the interactions between them.

The advantages and disadvantages of mediators are obvious: something reduces the coupling between concrete classes, allowing each type to be changed and reused independently. The disadvantage is that the intermediary's control will become more complicated with the complexity of the logic, which will make the intermediary bear too many tasks.

Usage: The intermediary pattern is used in the PureMVC framework to effectively isolate the coupling between the View layer and the Controller and Model layers. The View can effectively perform operations and data processing through the data proxy it holds.

Command mode: Encapsulate a request as an object (that is, the Command object we created), so that customers can be parameterized with different requests; queue requests or record request logs, and support revocable operations.

Abstract command Command: defines the interface of the command and declares the execution method.

Concrete command ConcreteCommand: The specific command implements the method to be executed. It is usually a virtual implementation and usually has a receiver (Receiver), the object that actually executes the command. Any class may become a recipient, as long as it can implement the corresponding functions required by the command.

Invoker: Asks the command object to execute the request. It usually holds the command object and can hold many command objects. This is where the client actually triggers the command and requires the command to perform the corresponding operation, which is equivalent to the entrance to the command object.

Client Client: The command is created by the client and the recipient of the command is set.

Usage: SimpleCommand and marcoCommand inherit the ICommand interface and implement the Execute method. Call SendNotifacation in the code, and then the notification will be notified to the observer in the View.NotifyObserver() method. The observer executes the ExecuteCommand in the Controller. The ExecuteCommand will get the command interface from the dictionary, call Execute to execute the rewritten command, and realize the solution. coupling.

Singleton pattern: The singleton pattern is the most commonly used design pattern. As the name suggests, a class has only one instance.

The singleton pattern provides a global access point, making program development more flexible, but the increase in singleton classes will also lead to excessive coupling of code, reducing reuse and maintenance.

Usage: In PureMVC, Facade, View, Model, and Controller all implement singleton mode, making batch access more convenient, and add the volatile keyword (volatile is mostly used in multi-threaded environments. When a variable is defined as volatile, When reading the value of this variable, it is read from momery every time instead of reading from cache. This is done to ensure that the information read from the variable is up-to-date, regardless of how other threads update this variable), providing mutual The calling interface.
 

Facade design pattern: During the development process, I need to obtain the View and Model objects in the Controller, modify the Model, update the View or obtain the Model in the View, and perform initialization operations. In the case of a lot of business logic, Model, There will be a lot of frequent calls between View and Controller, and the coupling will be enhanced. In PureMVC, an upper-layer interface is responsible for the management and operation of all core layers (Model, View, Controller). In PureMVC, use Developed the Facade design pattern.

Appearance design pattern definition: "Provide a unified interface for a group of subsystems or interfaces to simplify their complexity and reduce coupling."

The Facade class should be treated as an abstract class and never be instantiated directly. You should specifically write a subclass of Facade and add or override Facade methods to implement specific applications. This class is named "ApplicationFacade" (name it as you like, of course).

As mentioned before, it is mainly responsible for accessing and notifying Model, View and Controller. That is to manage these three.

Facade is the "broker" of Model, View and Controller. When actually writing code, you do not need to import the class files of these three, nor do you need to use them directly. The Facade class already contains the construction of the three core MVC singletons in the construction method.

The Facade class initializes the Model, View and Controller objects in the constructor and saves references to them in member variables.

In this way, Facade can access Model, View and Controller. In this way, all operations on the core layer are concentrated on the Facade, preventing developers from directly operating the core layer.

Initialize facade

Generally, actual applications have a facade subclass. This facade class object is responsible for initializing Proxy, Mediator and Command, establishing the mapping between Command and Notification names, or registering all Proxy and Mediator by executing a Command.

At this time, Command, Proxy and Mediator were mentioned. Their corresponding relationship is actually:

Proxy=>Model

Mediator=>View

Command=>Controller

What are Model and Proxy?

From the screenshot above, you can see the relationship diagram between Model and Proxy.

Model is data (Data Object), and the game is driven by data. For example, character data, including HP, MP, gold coins, levels, skills, etc., are managed through Proxy in PureMVC. Proxy is the proxy design pattern, which "provides a proxy for other objects to control access to that object." Proxy is used to control and manage the data model in PureMVC.

"Data Object" is an object that stores data in an arbitrary structure. In other words, we will not communicate directly with the Model. All additions, deletions, modifications and queries to the Model are handled through Proxy.

Regarding the Proxy agency model, for example, the Proxy agent of the star Cristiano Ronaldo is the Cristiano Ronaldo team. All business cooperation matters are handled through the Cristiano Ronaldo team. In this way, Cristiano Ronaldo does not need to face the cost of cooperation and communication from all directions alone ( Reduce coupling), at the same time, the team can also help Cristiano Ronaldo handle many matters, without the need for everything to go through Cristiano Ronaldo (hiding its internal implementation details to a certain extent), from a code perspective, it is also satisfying "Changing one does not affect the others." My modifications to some data should not affect other data.
Let's continue to look at the diagram above and look at the arrow of the Model. It only interacts with the Facade. As mentioned above, this is to reduce coupling.

The Objs next to it are Models, which correspond to Proxy, but one Model does not correspond to one Proxy. If so, it would be too cumbersome. For example, a module may include many different Model data, and you can define multiple different Model, but it can be managed through a Proxy, which is more convenient. 

Model data is usually obtained or set synchronously. Proxy may provide an API to access some properties or methods of DataObject, or may directly provide a reference to DataObject (but this is not recommended, we should protect the Model and provide corresponding interfaces for access). If a method for updating the DataObject is provided, a Notification will be sent to notify other parts of the system when the data is modified.

The Notification notification here is actually the observer mode. When an object changes, many other objects also need to respond to it. At this time, the observer mode is used, the publish-subscribe mode, such as We subscribe to a certain WeChat public account, and the public account publishes an article, and all subscribed users can receive reminders. This is everywhere in the game. When the Model changes, the View component is notified to update. Then in View, there will be corresponding methods to handle Notification notifications and perform corresponding logical processing.

Proxy only sends Notification notifications (when data changes), it does not process Notification notifications, and it does not care about how View components change.

The Proxy object should not notify the system that its DataObject (data object) has changed by referencing or operating the Mediator object.

In other words, the Mediator can obtain the Proxy, but the Mediator should not be obtained in the Proxy. If you want to notify the View layer to update, just send a Notification notification.

(Proxy does not care about how these Notifications will affect the system after they are issued)

In this way, there is only one-way coupling between Proxy and Mediator.

Proxy also contains certain logic processing parts. We implement Domain Logic (domain logic) in Proxy as much as possible, so as to separate the Model layer from the associated View layer and Controller layer as much as possible.

For example, if you implement a function that calculates tax deductions in a Mediator or Command, it is equivalent to coupling this part of the code. For example, your View needs to be rebuilt, or other Commands must also use the tax deduction function. , then this part of the code cannot be reused, so it is more appropriate to put it in Proxy.

About data type conversion

Because Model (Data Object) is usually a complex data structure, we often need to reference some of its properties and convert the type into the data we need.

Through getter and setter properties, it can help us solve this frequent type conversion problem very well.

It may be necessary to define multiple different types of getters to obtain certain parts of the Data Object.

public ArrayCollection searchResultAC 

 get {return data as ArrayCollection;}

 }

This is an example from the official document, but on the mobile side, it is still recommended to cache it to avoid consuming memory and causing GC by converting every time.

What are View and Mediator?

View is a view component. Like the Model layer, it is also managed through a "mediator". View is a specific view component (View Component) operated by Mediator. Including: adding event listeners, sending or receiving Notifications, and directly changing the state of view components.

Mediator (mediator design pattern):

"Use an intermediary object to encapsulate a series of object interactions" (heavy interaction, strong logic)

*View Component refers to various controls on the UI, buttons, lists, scroll bars, etc.

Doing this separates the view from the logic that controls it. The definition and initialization of components in View are defined and implemented in Mediator, so that even if the UI is rebuilt, only the Mediator is changed.

Because Mediator often interacts with Proxy, it is often necessary to obtain a reference to the Proxy instance in the Mediator's constructor and save it in the Mediator's properties, thus avoiding the performance overhead caused by frequently obtaining Proxy instances.

As you can see here, usually there is a one-to-one relationship between View and Mediator, but some Views are relatively complex and consist of multiple sub-UIs. Mediators can also have multiple View Component references (different sub-UIs for the same function).

But if the Mediator is too large, it needs to be split. It is better to process it in the Mediator of the split sub-module than to put it all together. This part of the work needs to be slowly restructured.

Convert View Component type

(This part is handled in the same way as Model)

This Mediator subclass will pass its View Component parameter to the parent class in the constructor, and it will be assigned to a protect attribute internally: viewComponent, and transferred to the Object type

After the Mediator is constructed, you can dynamically assign (modify) its View Component by calling its setViewComponent function. After that, every time you need to access the API of this Object, you have to manually convert the Object into its real type. This is a tedious and repetitive task.

Like the Model above, the reference to the View is saved in the Mediator, and we cache it.

What Mediator usually does:

1. Check or modify the properties of View Component

2. Check or modify the properties published by the Proxy object

3. Send one or more Notifications to notify other Mediators or Commands to respond (may even be sent to itself).

But one thing to note is that business logic (Business Logic) should be placed in the Command, not the Mediator!

In fact, Mediator does not handle complex logic. Like the Model, the domain logic part can be implemented in the Mediator, which reduces the coupling with the Controller and improves reusability.

Note: Do not regard detection or modification of VC (View Component and Proxy) properties as business logic (Business Logic)

Here are some helpful lessons:

1. If multiple Mediators respond to the same event, a Notification should be sent, and then the relevant Mediators will respond accordingly. (Observer mode)

(For example, you currently have 3 UIs displayed on the screen, and each UI displays the player's amount of money. When the money changes, the Proxy should send a corresponding Notification notification, and then the 3 UIs accept the notification and proceed. View updates)

2. If a Mediator needs to interact a lot with other Mediators, a good way is to use Command to define the interaction steps in one place.

3. A Mediator should not be allowed to directly obtain and call other Mediators. It is wrong to define such an operation in a Mediator. As you can see from the conceptual diagram above, there will be no direct communication between Mediator and Mediator, which defeats the original intention of reducing coupling.

When changes to a View will affect another View component, just send a Notification notification.

4. Proxy is stateful. When the state changes, it sends a Notification to notify the Mediator and reflects the data changes to the view component.

Implement these "requests" that are used multiple times through commands to make them more independent and improve reusability.

Proxy design pattern vs Mediator design pattern?

The two design patterns mentioned earlier do very similar things, but by definition, Proxy focuses more on controlling data access, which is equivalent to representing real data, while Mediator focuses more on data interaction (encapsulation) (interaction of a series of objects), strong logic, such as the middleman of the interaction between AB, then the interaction with the UI is relatively complicated and cumbersome, so Mediator is used to handle the operations on the View.

A pretty good example is given in "Dahua Design Pattern" to illustrate Mediator, that is, the United Nations, and similar environmental protection organizations. The real estate agents we have daily contact with are responsible for the survey, review, sale, tax payment, and transfer of houses. etc. (interactive, strong logic) work.

If we were asked to contact the landlord directly, we would not only have a lot of knowledge but also legal risks.

Another example is a 4S store. We go to the 4S store for sales, after-sales, etc. In the Mediator, A and B interact. Both A and B "know" the Mediator mediator. We go to the 4S store, and the 4S store is responsible. Talk to the car manufacturer.

What are Controller and Command?

The Controller saves the mapping of all Commands. The Command class is stateless and is only created when needed.

The Command design pattern is used here, which encapsulates a "request" and "behavior" into an object, and encapsulates the logical part independently to improve reusability. Modifications to the View or Mediator will not affect the Command itself. Through the Facade top-level interface, Proxy, Mediator, and Command can access and communicate with each other.

Command can obtain and interact with Proxy and Mediator objects, send Notifications, and execute other Commands. Often used for complex or system-wide operations such as application "startup" and "shutdown". The application's business logic should be implemented here.

The Facade needs to initialize the Controller at startup and establish the mapping between Notification and Command.

The Controller will register to listen for each Notification. When notified, the Controller will instantiate an object of the Command class corresponding to the Notification. Finally, pass the Notification as a parameter to the execute method. For details, please refer to the implementation of the Command base class.

In other words, the execution of Command is performed by sending Notification notification.

Command objects are stateless; they are created only when needed (the Controller receives the corresponding Notification), and are deleted after being executed (the execute method is called). So do not reference the Command object in those long-living objects.

During operation, you can initialize Proxy and Mediator through Command, that is, register them in Facade.

for example:

public class ModelPrepCommand : SimpleCommand {

 //Called by MacroCommand

 public override void Execute (INotification notification) {

 IFacade.registerProxy (new SearchProxy ());

 IFacade.registerProxy (new PrefsProxy ());

 IFacade.registerProxy (new UsersProxy ());

 }

(I won’t post a screenshot of the Command part here. Once everyone can understand the Model and Mediator, the Command part will be clearer)

Two types of Commands:

Command implements the ICommand interface. There are two classes in PureMVC that implement the ICommand interface: SimpleCommand and MacroCommand. SimpleCommand has only one execute method, which accepts an Inotification instance as a parameter. In practical applications, you only need to override this method.

MacroCommand allows you to execute multiple Commands sequentially. Each execution creates a Command object and passes a reference to the source Notification. MacroCommand calls its own initializeMacroCommand method in the constructor. In actual application, you need to override this method and call addSubCommand to add a subCommand. You can combine SimpleCommand and MacroCommand into a new Command.

Others to introduce:

Business Logic (Business Logic) and Domain Logic (Domain Logic)?
 

There are many places in your program where you can place code (Command, Mediator, and Proxy); inevitably you will come across the question: Which code should go where? What, exactly, should Command do?

The logic in the program is divided into Business Logic (Business Logic) and Domain Logic (Domain Logic). First, you need to know the difference between the two.

Business Logic (Business Logic) needs to coordinate Model and View State (View).

Model ensures data integrity and consistency by using Proxy. Proxy centralizes the Domain Logic of the program and exposes the API for operating data objects. It encapsulates all operations on the data model, regardless of whether the data is client-side or server-side, and whether the data access is synchronous or asynchronous for other parts of the program.

Mediator and Proxy can provide some operation interfaces for Command to call to manage ViewComponent and Model (Data Object), while hiding the details of specific operations from Command.

Observer 与 Notification ?

PureMVC's communication is implemented in a loosely coupled way using the Observer pattern, a design pattern almost everywhere in game development. You only need to use a very simple method to send from Proxy, Mediator, Command and Facade. Notification, you don't even need to create a Notification instance.

Facade and Proxy can only send Notification. Mediators can both send and receive Notification. Notification is mapped to Command, and Command can also send Notification. This is a "publish/subscribe" mechanism where all observers receive the same notification. For example, multiple book and periodical subscribers can subscribe to the same magazine. When a new issue of the magazine is published, all subscribers will be notified.

Facade saves the mapping between Command and Notification. When a Notification is issued, the corresponding Command will be automatically executed by the Controller. Command implements complex interactions and reduces the coupling between View and Model.

Define Notification constants

When these Notification name constants need to be accessed by other programs, we can use a separate "ApplicationConstants" class to store these Notification name constant definitions. No matter when, the Notification (notification) name should be defined as a constant. When you need to reference a Notification, use its name constant. This can avoid some errors that cannot be found during compilation. Because the compiler can check constants; with strings, if you enter the wrong string by mistake, the compiler has no way of knowing and no way of reporting an error.

Mediator sends, declares, and receives Notification

When a Mediator is registered with a View, the Mediator's listNotifications method will be called to return all the Notifications that the Mediator object cares about in the form of an array. Later, when other roles in the system issue a Notification with the same name, the Mediator that cares about this notification will call the handleNotification method and pass the Notification to the method as a parameter.

Here the Mediator is the notifier. When the Proxy data changes, the Mediator receives the notification and updates the UI.

Proxy sends, but does not receive Notification

In many situations, Proxy needs to send Notification (notification). For example: when Proxy receives data from a remote service, it sends Notification to tell the system; or when Proxy's data is updated, it sends Notification to notify the view component to update, etc.

If Proxy is also allowed to listen to Notification (notification), it will cause its coupling with the View (View) layer and the Controller (Control) layer to be too high.

View and Controller must listen to the Notification sent by Proxy, because their responsibility is to enable users to interact with the data held by Proxy through a visual interface. However, changes to the View layer and Controller layer should not affect the Model layer.

Finally, there is a small example of the interaction between View Component and Mediator in the document:

Suppose there is a LoginPanel component with a form. There is a corresponding LoginPanelMediator, which is responsible for interacting with the LoginPanel and sending login requests in response to its input information.

The collaboration between LoginPanel and LoginPanelMediator is as follows: LoginPanel sends a TRY_LOGIN event when the user completes inputting information and wants to log in. LoginPanelMediator handles this event by sending a Notification with the LoginVO contained in the component as the "report body". .

The LoginPanel.mxml part is omitted here, and the view parts are different.

The LoginPanel component contains a newly created LoginVO object with user form input. When the user clicks the "Login" button, an event is triggered, and the next thing is taken over by the LoginPanelMediator.

In this way, the role of View Component is to simply collect data and notify the system after collecting the data. What can be improved is to make the login button available (enabled) only when both username and password have content, so as to avoid malicious login.

View Component hides its internal implementation from the outside. The entire API used by Mediator includes: a TRY_LOGIN event, a LoginVO attribute and the status attribute of Panel.

Final summary:
 

There is a one-way dependency between View and Model. View must know what Model is, and View also displays the content on the view based on Model data. The Model doesn't care about the content on the View.

Proxy and Mediator are both agents and mediators in their responsibilities, responsible for communicating with other components. And their registration is done by

Facade for unified management.

Proxy and Mediator should not contain a lot of business logic. The business logic part should be handled in Command, and some operations on the data itself should be placed in Proxy and Mediator.

Although any Proxy can be accessed and modified in the Mediator, it is not recommended. Let the Command do these tasks to achieve loose coupling between the View and the Model. This way the Command can be reused by other parts of the View.

 material:

Official GitHub connection: https://github.com/PureMVC

PureMVC official website: www.puremvc.org

Blog: PureMVC analysis_Peter_Gao_'s blog-CSDN blog_puremvc

Blog: Application of PureMVC framework in Unity (1)_Peter_Gao_'s blog-CSDN blog

Guess you like

Origin blog.csdn.net/qq_40097668/article/details/123987708