This is a large-scale desktop Edition program. There are hundreds of people fighting on each component of the software before and after it. The content here is basically the part I have left behind, or I am involved in the design, you can also reuse or transform these components.
Plug-in architecture
This structure is clear and mandatory for many projects. The plug-in has high flexibility and scalability, which is an outstanding advantage of this architecture.
A program using this architecture usually has two methods:
One way is to load each separate project (separate DLL) as a separate plug-in, and complete initialization during the loading process, after all the plug-ins are loaded, initialize the Program Interface Based on the plug-in status. This is also the practice of this project.
Another approach is dynamic loading. The program monitors a specific directory. When there are changes in the directory, such as adding or deleting a DLL that can be used, dynamically load or uninstall the DLL, and then modify the relevant menu and interface. This is a bit like the so-called "Hot swapping" in hardware.
Animation Design
This part of the design actually contains three related content:
1. Rendering Method
This design is based on the override object, which records data changes. When the data changes, we do not directly modify the members of the original data object, instead, create an object to save this change.
For example, we have an object named rectangle, which has a member data to indicate its fill color. When we create it, we set it to Red directly, and when we change its color to green, our internal implementation is to create a coloroverride corresponding to the rectangle object, which records the ID and green of the rectangle.
During rendering, we first check whether the data object has related override. If so, the data object has the highest priority and is used for rendering. If not, the data object is rendered based on its own attributes.
2. Generation of animation Elements
The generation of animation elements (action objects) is implemented based on the override design.
In simple observer mode, we create an actionfactory object to listen to override creation, update, and deletion events. Based on override changes, we can capture the relevant animation data and save it to the action.
This idea works well until the emergence of transformaction, because when the user moves the object, the user input and the obtained override are no longer exactly the same. For example, although the final matrix is the same for a screw rotation of 360 degrees and 720 degrees, the user's expected results are different. The 360 degrees indicate a revolution, 720 degrees means two turns. In addition, the transfer of 90 degrees is different from the transfer of 270 degrees for users, although the final location is the same.
In this case, listening for override alone is not enough. At this time, we need to fully record the user's input data. Based on the real user data, combined with the value of override, we can correctly restore the user's intent.
3. Play the animation
The animation playing process obtains the attribute value (entityattribute object) of the generated animation element action based on the time point, and then updates the corresponding override according to the value, in this way, the animation effect is continuously cyclically achieved.
These three parts can be combined to complete a complete animation production and playback function.
Command mode
The design of this system is also quite satisfactory. It mainly includes the following points:
1. When the program starts, commanddefinition in all plug-ins will be initialized. The class name is used to dynamically create commands.
2. The command uses a string as the unique identifier. When executing the command, you only need to specify this identifier in commandexecutor. The decoupling effect can also be achieved by passing strings.
3. After the commandexecutor obtains the string, it finds the relevant commanddefinition object based on the identifier, creates the command and commandparameter objects, and then executes the command.
4. During command execution, the data in the model layer is not directly modified, but the request object provided in the model layer is called to modify the data in the model. This makes it easy to maintain the stability of both interfaces.
Undo implementation
Undo/Redo is a function provided by many systems, and there are many implementation methods. As far as I have experienced a system, there are two similar practices:
The first approach is to provide the Undo/Redo method for the command object. When executing command, save it to a stack. When executing Undo/Redo, call the Undo/Redo method of command.
The second approach is the practice in this project. During command execution, a new transaction is created. After the command is executed, the transaction also creates the change and then submits the change. The submitted transaction is also saved to a collection. When Undo/Redo is performed, the corresponding transaction is searched and then undo/Redo is performed.
In implementation, undo/Redo itself is also a command, but it is not recorded in the execution set.
Event Mode
This is a typical observer model, but there are two interesting practices:
1. You can subscribe to all types of events. In the previous system, we subscribe to an object event. In the implementation of this system, we can subscribe to a class of Object events. This is interesting and proved useful. Implementing such a function is no different from implementing a simple observer, except that when an event is triggered, it turns around and some objects can be specially processed.
2. All related uis listen to model events and update them. This is a normal operation. The difference is that all the UI operations provide the preview effect. At this time, only the data on the UI is modified, and the model is modified only when it is submitted, then the model sends an event to update all the UIS.
Saveload implementation
This part is nothing special, that is, the application of the accessor mode, which stores the tree object structure that needs to be persisted in the file into binary format and XML format. This tree object accepts an accessor, which provides an extension point.
Architecture reflection case-desktop program architecture case