Interface assembler mode

xiaoxiao2021-03-31  209

This paper proposes an architectural mode in interface design - interface assembler mode, which is committed to decomposing interfaces, decoupling interface and assembly behavior, and understanding interface logic processing and domain logic, so we are developing a GUI fat client. The interface can be freed from many interface control management while focusing on our background business logic. Through this mode, we can dynamically assemble our interface, we can even easily insert Transaction transactions or session session management in our interface.

This article will explain this mode by analyzing the process of designing an architecture, starting from a simple design model, step by step to a complete architecture. It is also showing you an architectural thinking course to you. In addition, this paper gives examples of Eclipse SWT (STANDARD WIDGET TOOLKIT).

Problem

Interface design is often the root cause generated, whether it is architectural mode or design mode, such as MVC mode, Observer, Facade, etc., is also a driving force for the entire software industry. Unfortunately, even today, the interface design is still one of the difficult-breaking bottlenecks in software design. We have used Java Swing or Eclipse SWT to do projects, it is difficult to decompose interfaces. It is not like our business logic, which can easily break down in different classes in different classes, because each business The coupling between logic is very low. However, interface logic is different. You can't appoint a text box to the other class, and each interface element is interdependent, unable to remove coupling, the general approach can only be triggered in the interface element (For example, a button click event), the input data is encapsulated into a data object to pass the logic processing class to the background.

Eclipse's Wizard Framework provides a good practice on interface decomposition, which can separate the button zone and other interface regions, and implement the Wizard framework with similar MVC. But this implementation is not a flaw, a disadvantage is that Wizard is a plug-in, which reduces reusability and cannot be ported to the environment other than ECLIPSE. Another disadvantage is that it introduces a lot of complexity, and in some of the ability to lose some fine control over the control of the interface element, it may be its excessive emphasis on the convenience of automation and user expansion. For example, the user cannot put its logic into the button event control in the button area, and can only set the button zone in the interface element listner of the custom area. If the user-defined interface element, there is a lot of listener. The combination determines a button state (if "next step"), this is very influential, and there is no more complex logic judgment, that is, the logic that only needs to be processed in the button Listener event now Treatment in Listener dispersed in each interface element. This is also a common problem worth repeatedly emphasized: When you want to keep the architecture or design perfection, you will inevitably lose other features at the expense. There is always no perfect thing in the world, we only pay attention to our.

The inspiration of this architecture pattern I have to make is from a real project, an item modeling with RSA (RSA (RSA Software Architect) / Eclipse, in the RSA environment, must be under a unique context Operation, this means that I must package the input data before starting the interface, and return to the output data after shutting down, rather than direct processing data, and the input / output data object must be packaged. As mentioned earlier, this situation is common in interface design. So, when you name it, I use the wording of the assembler -assembler, one means to enter the assembly of input / output data objects, and another layer is the assembly of interface components (the collection of interface elements), and the assembly is deeper here. The hierarchical meaning is that the interface component can be assembled during runtime. And this mode can be implemented in any language (Java, C , etc.). Here I will start from a simple design model, step by step to a complete architecture. It is also showing you an architectural thinking course to you. An example of Eclipse SWT (Standard Widget Toolkit) is given herein. Back to top

Interface decomposition

In Eclipse SWT, there are several important interface components, one is the outermost container of the shell-interface, similar to the frame in Java Swing, the other is a collection of Composite-interface elements, similar to the Java Swing, PANEL. Our interface decomposition will start from Composite, (shell itself does not need to decompose). We can match an empty Composite in Shell, and then our specific interface elements are defined in this Composite. This will separate the Composite logic from the shell, so we have 2 classes now (now we use the concept class):

Figure 1. Separate the Composite logic from the shell

Editor: This class handles the logic of the shell, such as display -show, close -close, it is responsible for creating and destroying EditorComposite.

EditorComposite: This class handles the interface logic of Composite, such as creating an interface element.

There are two points worth noting that first, editor is responsible for the creation and destruction of EditorComposite, which is the management of life cycle. Then we can think that if our interface needs Transaction-transaction or session-session management, we can completely let Editor are responsible for this responsibility, rather than scattered in each EditorComposite. How to extend the transaction function of the interface may be complicated, which has exceeded the discussion scope of this article, I just analyze the possible scalability from the level of the architecture. Second, an editor can include multiple editorcomposite, such as our property page, at this time, the empty composite defined in the shell will be a TabFolder. There is a situation, that is, we can determine if we need according to some logic. Which EditorComposite is assembled. This requires us to have an assembled behavior.

Back to top

Interface component assembly

When our assembly logic is simple, we can define an assemble () method to be responsible for assembly behavior. But when our interface needs to assemble a series of EditorComposite, you will involve selection logic, select logic is not necessarily complicated, but we should also separate this behavior from Editor, so Editor can focus on interacting with users. The responsibility, and assembly behavior is assigned to a new class editorassembler, so there is a benefit, that once we have new EditorComposite needs to be added, we only need to change the EDITORASSEMBLER code without modifying the EDITOR code, this The change is isolated, and the modification of Editor is turned off, and the extension of assembly behavior is open. This is the basic principles for the object-oriented design area - Open-Close Principle. The reconstructed architecture is shown below: Figure 2. Reconstructing architecture

EditORASSEMBLER: This class handles EditorComposite creation, as well as multiple EditorComposite selection logic.

We can use IF / ELSE or SWITCH / CASE to hard encode. If logic is not very complicated and will not be too frequent in the future, it is enough to use this method, of course, you can consider loading multiple EditorComposite The information is specifically stored in a resource / information class, which is very effective in editorcomposite, so that each addition editorcomposite needs to change this resource class, this is a very useful modeling principle (to simplify our core Model, I don't expressed this resource class here).

If further considering our assembly logic is complicated, it is easier to change, even dynamically changed during runtime, we can store numerous editorcomposite and complex logic in a metadata file, such as XML or profile. In this way, new EditorComposite needs support, or modifies the installation logic, you do not need to modify the EditoSsembler class, just modify the metadata file. This will be able to configure our interface very dynamically. Here, there will be an architecture weighing problem. The metadata has its advantages, and there is also its shortcomings. First, it must be prepared to resolve it, the complexity has increased, and its second, it is also its advantages, it is also its shortcomings. We can modify the XML or profile, and only know the abnormality at runtime, it is also possible to deliberately destroy it. So we only need to modify the configuration of EditorComposite or frequently need to increase EditorComposite when you really need to increase EditorComposite. Here I tend to adopt resource classes.

Back to top

IO data assembly

The model design is here, we seem to lack the modeling of data streams. In a standard interface program, we will have a set of output data, such as pressing the "OK" button, we need to enter the input information on the interface elements. Output to background logic classes to process or directly call several logical classes to handle different interface elements. We generally habitually passed this data directly to logical classes. This do three shortcomings: 1. If our data read and write processing requirements must be performed in a specific context, we cannot call the background logic to process classes directly in the interface. In fact, this limitation is not rare, and in some development of the underlying (such as a protocol layer), it will often encounter only the case where you cannot read. Second, the replacement of the UI is poor. If we need a solution in the future, it can replace different UIs at runtime but the data output is the same, that is, the background logic processing is completely consistent, then this situation we need every A UI goes by calling the background logic class, repeats the encoding, and may have a logical class due to the programmer's mistake, resulting in a completely identical behavior class with a few inconsistency, not only serious violations Object design, and it is also possible to produce unpredictable bugs, which is difficult to maintain. Third, the reusability of the UI is poor, for examples of multiple UIs corresponding to a logic process, since the UI depends on the background logic class, if we want to modify the logical class in the future, we need to modify each UI. If we still have a need to support a UI requires different background logic classes in different environments, we may have to set a property specifically to identify the logical class to be used in the background. This will be complicated. Solving the above shortcomings is only one way to decouple the background logic class with the UI. If we pack the output data to be processed into an output data object from the interface unified output, then the UI's caller decided which background logic class is called to process the data, not the ui to determine the call behavior.

There is also an input data object to understand. When we call the UI, you may have dynamically load data from the environment you need to have some of the following lists, and some of our last configuration data is updated. It also needs to import existing data. So we need an input data object. This gives the following model:

Figure 3. Enter data object

InputDataObject: This class encapsulates input data. Responsible for parsing this data by EditorComposite.

OutputDataObject: This class encapsulates the output data. These data is generated by EditorComposite.

Editor is responsible for transmitting these two data objects.

Back to top

Reconstruction architecture

From the above model we can see that the Editor class is actually equivalent to a facade, all interfaces and users' interaction is responsible for centralized scheduling management, Editor will assign the assembly line to the EditORAsseMbler class, it is also responsible for temporary storage input and output Data, of course, if we have processing similar to transaction or session, will be delegated from the Editor to other relevant classes. Apply a FACADE design pattern, we can change your name to editorfacade, which is more designer's intention, don't ignore the name of the class, design is a serious science, every detail we can't, the architecture The design is more rigorous. Naming can play a role of communication, but also a reminder, EditorFacade reminds us to add new behaviors to add new behaviors to remember that it is a Facade, which cannot be assigned unconfiguring responsibilities.

Also, I found that after adding the InputDataObject class, EditorComposite has two responsibilities: loading interface element initialization data (some input data that needs to be dynamically obtained from the environment, get data from the InputDataObject object) and display the previous edited data (also InputDataObject object is obtained, we define two methods to process: loadDataInfo () - load initialization data; showPreInfo () - Displays the previous edited data. Of course, these two methods are generally private -Private, because this is the internal logic of EditorComposite itself, but we have made it public -Public -Public, because we can centrally control it in the EditoSsembler class. Call, and each editorcomposite will have a load initialization data and display an existing data. So why not abstract it, so that the EDITORComposite's development provider is more clear, although it is a bit destroying the encapsulation of EditorComposite. The privacy of the method, but this destruction is suitable from the perspective of architecture. Take a look at the previous EditORASSEMBLER class, it has two responsibilities, one is to create EditorComposite, and one is to select a judgment logic from several EditorComposite. If we decoupled these two unsatisfactory responsibilities to apply the Factory design mode, you can appoint the work of creating EditorComposite to an EditorCompositeFactory.

After the above reconstruction, the following conceptual model model is obtained:

Figure 4. Conceptual model model

Back to top

Implementation architecture

After the above analysis modeling, we can start the architecture, from the concept model we can easily abstract the corresponding interface. First, let's take a look at the EditorFacade class, based on our discussion, different interfaces may have different needs, such as support Transaction-transactions, then the EDITORFACADE is different, so we need to extract an interface to express The following is listed below IeditorFacade:

Listing 1: IEditorFacade.java

Public interface ieditorfacade {

Public void show ();

Public IinputDataObject getInputdata ();

Public void setInputData (IinputDataObject InputData);

Public IoutputDataObject GetputData ();

Public void setputPutdata (IoutputDataObject OutputData);

Public boolean isfinishedok ();

Public Composite Getrootcomposite ();

Public void setassembler (IEDITORASSEMBLER ASSEMBLER);

Public void close (Boolean Status);

}

Then the part of the EDITORFACADE class is as follows:

Listing 2: EditorFacade.java

Public Class EditorFacade Implements IEditorFacade {

PRIVATE shell shell;

// Validate if Editor is closed with ok or cancelprivate boolean finishedok;

// Input data

Private IinputDataObject InputData;

// Output Data

Private IoutputDataObject Outputdata;

Private composite composite;

Private IEDITORASSEMBLER ASSEMBLER;

Private void createsshell () {

Shell = new shell ();

Shell.setLayout (New GridLayout ());

CreateComponent ();

}

Private void createcomponent () {

Composite = New Composite (shell, swt.none);

.........

askEMBLER.CREATE (THIS);

}

Public void show () {

THIS.SHELL.OPEN ();

askPREMBLER.SHOWPREINFO ();

}

Public EditorFacade (IediToSsembler assembler, IINPUTDATAOBJECT INPUTDATA) {

THIS.ASSEMBLER = Assembler;

THIS.INPUTDATA = INPUTDATA;

this.createsshell ();

}

Public Composite Getrootcomposite () {

Return Composite;

}

Public void close (Boolean Status) {

Finishedok = status;

THIS.SHELL.CLOSE ();

}

}

Next, we define two IO data classes, it is clear that different interfaces have different input and output data, where we can only define two abstract interface IinputDataObject and IoutputDataObject, which inherits serialized Java. IO.Serializable interface, there is no other content. Here, it is not meaningless here, it can play a role in identifying, in addition, it hides the specific implementation, and if the delivery person does not have to know the specific data content, such a passage class has better reuse. Moreover, the specific data category does not have to expose the class that should not be known - the passer class, this is another basic principle-oriented basic principle - Dimit France (LOD): Don't talk to strangers. The list of IinputDataObject is given below:

Listing 3: IINPUTDATAOBJECT.JAVA

Public Interface IinputDataObject Extends Serializable {

}

Next, let's take a look at the implementation of the Editorassembler class. According to the previous discussion, it encapsulates the assembly logic of the interface. It will be modified, then we need an interface IEDITORASSEMBLER to regulate its behavior, here I gave it here. A abstract class AbstractItoassembler implements a method of loading a single editorcomposite, and I also give a specific EditorasseMbler class. This is an example in which only one editorcomposite is loaded. The code list is as follows:

Listing 4: IEDITORASSEMBLER.JAVAPUBLIC Interface IEditoSsembler {

/ **

* Create Editor Body and Init

* @Param Editor

* /

Public Void Create (IeditorFacade Editor);

/ **

* Create Editor Composite

* @Param Editor

* @Param CompositeClassID

*: Composite Class Name, E.G. Test.view.testComposite

* @Return

* /

Public IEditorComposite CreateComposite (IeditorFacade Editor,

String compositeClassID;

/ **

* Show exist info in ui for update.

* /

Public void showpreInfo ();

}

Listing 5: AbstractTeditorassembler.java

Public Abstract Class Abstractditorassembler Implements IEditoSsembler {

Public IEditorComposite CreateComposite (IeditorFacade Editor,

String compositeClassID) {

IEDITORCMPOSITE BODY;

Body = editorcompositeFactory.createComposite (CompositeClassID, Editor);

Body.create (editor.getrootcomposite ());

Body.seteditor (editor);

Return body;

}

......................................

}

Listing 6: StandaloneEditorassembler.java

Public Class StandaloneEditorassembler Extends AbstractTractedItoSsembler {

PRIVATE STRING Compositive;

Private IEditorComposite Bodycomposite;

/ **

*

* @Param CompositeClassID

*: Composite Class Qulified name, E.g. com.ibm..xxcomposite;

* /

Public StandaloneEditoSembler (String CompositeClassID) {

This.CompositeClassID = compositeclassid;

}

Public Void Create (IeditorFacade Editor) {

Bodycomposite = CreateComposite (Editor, CompositeClassID);

IF (Bodycomposite! = NULL)

Bodycomposite.loadDataInfo ();

}

Public void showpreInfo () {

Bodycompositive.showPreInfo ();

}

}

Next, it is an implementation of EditorCompositeFactory. This class is relatively simple, just generates classes according to the class name:

Listing 7: EditorCompositeFactory.java

Public class editorcompositivefactory {/ **

* CREATE IEDITORCOMPOSITE

* @Param clsname

* @Param Editor

* @Return

* /

Public Static IEditorComposite CreateComposite (String Clsname,

IEDITORFACADE Editor) {

IeditorCompositive Composite = NULL;

Try {

Class CLS = Class.Forname (clsname);

IF (CLS! = NULL)

Composite = (IeditorComposite) CLS.NEWINSTANCE ();

} catch (exception e) {

E.PrintStackTrace ();

}

IF (Composite! = null) {

Composite.seteditor (editor);

}

Return Composite;

}

}

Finally, it is the implementation of EditorComposite, it is clear that EDITORComposite each interface is different, so we only define an interface here to specify behavior, and the specific EditorComposite implementation I will give in the test package in the code attachment.

Listing 8: IEDITORComposite.java

Public interface IEditorComposite {

/ ** SET UP Composite UI * /

Public Void Create (Composite Parent);

/ ** set the current editor for shell close and data set * /

Public void setEditor (IEditorFacade Editor);

/ ** Show previous data information in ui * /

Public void showpreInfo ();

Public void loadingdataInfo ();

}

Below, we write some test code to test it. This test application is to write a phone book. For the sake of simplicity, I only define a EditorCompositive-PhoneBookComposite, which is only exemplarily changed to change the title of the interface when writing assembly logic. size. (Detailed code shows the code download)

Listing 9: PhoneBookedItoSsembler.java

Public Void Create (IeditorFacade Editor) {

IF (compositeTYPE == 0) {

// IT is a phone book.

Bodycomposite = CreateComposite (Editor, "Test.PhonebookComposite");

Editor.getshell (). Settext ("Phone Book");

Editor.getshell (). setsize (400, 300);

Editor.getshell (). Redraw ();

IF (Bodycomposite! = NULL)

Bodycomposite.loadDataInfo ();

} else if (compositeTYPE == 1) {

// IT is a memo book.

Bodycomposite = CreateComposite (Editor, "Test.PhonebookComposite"); editor.getshell (). settext ("Memo Book");

Editor.getshell (). Setsize (500, 300);

Editor.getshell (). Redraw ();

IF (Bodycomposite! = NULL)

Bodycomposite.loadDataInfo ();

}

}

Listing 10: main.java

Public static void main (String [] args) {

// Define the Phonebook EditoSsembler.

Ieditorassembler assembler = new phonebookeditorasembler (0);

// Define PhoneBook input data

IinputDataObject InputData = New PhoneBookInputDo ("LYL", "010-8000100");

// Define Phonebook Editor

EditorFacade Editor = New EditorFacade (Assembler, InputData);

Editor.show ();

IF (editor.isfinishedok ()) {

// Remove the PHONEBOOK output data.

IF (editor.getputddata () instanceof phonebookoutputdo) {

PhonebookOutputdo OutputData = (PhonebookOutputdo) Editor

. GetoutputData ();

String name = OutputData.getname ();

String phone = outputdata.getphone ();

System.out.println ("Name:" Name "; Phone:" Phone);

}

}

}

Next, we can look at the realization model of the architecture. Note that I use a layered approach when I use the UML map below, all the interfaces will be on the top floor, this layered Picture UML The method of the figure helps us to clarify the ideas of the architecture, which is also easy to communicate with other members of the development group.

Figure 5. Implementation model of architecture

At this point, we have completed the implementation of the core architecture of the interface assembly, pay attention, is only an implementation, not all of the interface assembly mode, as a model, it must have a wider extension, below we will explore its mode Nature.

Back to top

Mode and values

This mode is a architectural mode, and there are three elements of the model: problem, environment, solution, which in front of us has discussed in detail, here we discuss other parameters. Each mode has its own unique values, then interface assembler mode provides us with what values?

First, its essence is in this decomposition interface, decoupling interface and assembly behavior, which is very beneficial in applications with multiple interfaces, when the interface is more, if there is no more concentrated scheduling control method To manage these interfaces, it will form interface behavior that cannot be specified, and the style is different, it is more difficult to treat Transaction transactions or session session control. This may not be obvious in small application development, but controlling the dispersed irregular interface behavior in a large and medium-sized application will be a nightmare, and the entire development group may be immersed in bug repair and interface. In the modification, there is no need to write in the field logic code. By decoupling interfaces and assembly behaviors, developers can concentrate on the development of interface logic and domain logic without each interface to write code of the management interface. In fact, this is also an advantage of modelization. Models can optimize our architecture to standardize development behavior, so it will save development costs. Second, it will decouple interface logic processing with domain logic processing (that is, data logic processing). We extracted data input and output from the interface model, without coupling interface, this gains a huge benefit, first, we can handle data outside the interface, handling this data in our domain class, that is The interface only provides a carrier that defines the data, and these data is used by domain logic classes, while the main energy we develop should also be placed on the domain class of processing business logic. Second, now we will decouple interface and domain types so that our interface and domain categories can be independently changed, there is no dependence, which is very convenient for our developers' division, and write interface development groups. Depending on the development group written in the background logic class. Third, when doing unit test-UIT TEST, developing a background logic class can test the domain class separately, while the personnel of the development interface can also test interface logic separately. Fourth, when we have multiple interface mechanisms, our background logic categories can be easily connected. For example, we have to support GUI (SWT / Java Swing) and web ways, then our domain class and data classes do not need any Changes can be easily switched. Fifth, we can also get the benefits, it is reusable for the data class. If we don't enter the package behavior of the output data class, we may scatter each data directly in the interface class, so when you want to change one This part of the logic must be rewritten when the interface mechanism is not reused.

As a model, there will be many variants, that is to say it is not in this external implementation, which we give, there are other implementations, in example, we just assemble an editorcomposite, we can of course once Assemble a few EditorComposite, such as a complex interface, there are several editorcomposite components, or like a property page, and there are several editorComposite, we only need to implement one packaging assemblerBler. Alternatively, we can dynamically switch interfaces between several interfaces during the run interface, which may be complex, which is also limited to platforms or language, but is not not implemented.

For the applicability of this mode, I think it is mainly suitable for situations of those who are loaded with an EditorCompositive or property page, as for the implementation architecture of the Wizard wizard interface, but also need further exploration, but from this mode concept level It is a key value that can be used to implement the Wizard wizard interface, but it may change the current architecture when specific implementation. In addition, this mode is mainly suitable for the GUI client interface, and there is already other modes for the web-form interface. We can also discuss the relationship between interface assembler mode and other models. In the interface architecture, we already have a famous MVC mode. Why do you need an interface module mode? Although the MVC mode is also decoupled with the logic processing of the interface, its starting point is mainly for a few interfaces after a business logic processing, it is necessary to update the display, which means that its contribution is his timely communication data change. The ability, this is inconsistent with our model, we mainly solve the problem of interfacing assembly and data stripping, of course, they are in a substantial similarity, our EditorFacade is a point in the MVC.

Back to top

Conclude

The interface assembler pattern described in this article provides us with a decoupling of interface and assembly behavior, and decoupled interface logic processing and domain logic processing, in the GUI fat client interface, the author is already in several It applies it in large projects, so its feasibility is practical. Of course, any model, whether it is design mode or architecture mode, has its applicability, only suitable, there is no absolute advantage, we have the application mode is that the model is expected to meet our needs, Not because of other reasons.

转载请注明原文地址:https://www.9cbs.com/read-130884.html

New Post(0)