Implement the plugin architecture in the C # program - Sunmast translation
Original link: http://www.cuj.com/documents/s=8209/2020301walcheske/
Original author: Shawn Patrick Walcheske
Translator: Electronic Science and Technology Xia
[introduction]
The C # language under the .NET framework provides a lot of powerful features and mechanisms like other .NET languages. Some of them are new, and some are moving from the previous language and platform. However, this kind of clever The combination has produced some interesting methods to solve our problem. This article will tell how to use these wonderful features, the Plug-INS mechanism to establish scalable solutions. Back will also provide a brief Examples, you can even replace independent procedures that have been widely used in many systems. In a system, there may be many programs often need to perform data processing. There may be one of the programs to handle the information of employees. The other is used to manage customer relationships. In most cases, the system is always designed as many independent programs, and there are few interactions between them, often using replication code methods to share. And actually this situation These plugins can be managed to manage those programs as plugins. This design allows us to share a public approach in different solutions, providing a unified method.
Image One is a screenshot of an example program. The user interface and other common programs are not different. The entire form is vertically divided into two pieces. The pane on the left is a tree menu, used to display the plugin list, in each plugin The branch is listed below, the data managed by this plugin is listed. The pane on the right is used to edit the data of the selected plugin on the left. Each plugin provides the interface of the respective editing data. Image shows a delicate work area .
[Start]
Then, the main program must be able to load the plugin, then communicate with these plugins, so that we can implement our design. All of these implementations can have many different methods, depending on the language and platform selected by the developer. If you choose C # And .NET, the reflection mechanism can be used to load the plug-in and their interfaces and abstract classes can be used to communicate with the plug-in.
In order to better understand communication between main programs and plugins, you can first understand design mode. The design mode is earlier by Erich Gamma [1], which uses architecture and object ideas to implement universal communication model. Regardless of whether the component has different Input and output, as long as they have similar structures. Design patterns can help developers use a widely proven to solve the problem. In fact it is the language describing the solution, not specific details or programming languages of the problem Details. The key point of the design mode strategy is how to decompose the entire solution according to the function, which is done by executing the different functions of the main program. This main program and subroutine can pass through the design well The interface is completed. We can immediately get these two benefits through this decomposition: First, the software project is divided into a smaller unhealthy unit, and the design of the workflow can be easier, and the smaller code sniffer means code. It is easier to establish and maintain. The second advantage is that when changing the program behavior, it will not be related to the operation of the main program. The main program does not have to care about the subroutine, and the general communication mechanism is sufficient.
[Establish an interface]
In the C # program, the interface is used to define a class of features. The interface defines the expected method, attribute, event information. In order to use the interface, each particular function must be strictly followed by the definition of the interface to complete the functions described in the interface. List A display showing the interface of the above example: IPLUG. This interface defines four ways: getData, getEditControl, Save and Print. These four definitions do not describe how to complete, but they guarantee that this class supports IPLUG interface. That is to guarantee calls to support these methods.
[Custom property]
Before viewing the code, the discussion is always transferred to the property custom above. Properties customization is one of the very great new features provided by .NET, attributes are a common structure for all programming languages. For example, A function for identifying accessible access, private, or protect flag is an attribute of this function. The reason for customization is so excited, that is because programmers will only provide limited properties available from the language itself. Concentrated selection. A custom property is actually a class, which inherits from System.attribute, which is allowed to be self-description. Attribute customization can be applied to most structures, including C #, methods, events, events, Domain and attributes, etc. The sample code snippet defines two custom properties: PlugDisplayNameAttribute and PlugDescriptionAttribute, all the categories inside the plugin must support these two properties. The list is used to define the class of PlugDisplayNameAttribute. This property is used to display plugins The content of the node. When the program is running, the main program will be able to obtain the attribute value using the reflection. [Plug-INS]]
The above sample program includes two plug-ins. These plugins are defined in Employeeplug.cs and CustomerPlug.cs. The list 3 shows the partial definition of the EmployePlug class. Here are some key points.
1. This class implements an IPLUG interface. Since the main program does not know how the category inside the plugin is defined, this is very important, the main program needs to communicate with each plug-in. This design uses the object-oriented concept "Polymorphism". When the polymorphism allows time, you can call the method in which the derivative class can be invoked by reference to the base class. 2. This class is identified by two attributes, so the main program can determine that this plugin is not effective. In C #, you have to identify an attribute to a class, you have to declare the properties before the definition of the class, the content is attached to the parentheses .3. Simply, the example only uses data directly written directly. And if this plugin It is a formal product, then data should always be placed in a database or file, and all the data should be managed by the plugin itself. The data of the readoreEPLUG class is stored here with the Employeedata object, which is also a type and implemented. IPLUGDATA interface. IPLUGDATA interface is defined in iPlugData.cs, which provides the most basic data exchange function for communication between main program and plug-ins. All objects that support IPLUGDATA interfaces will provide a notification when the underlying data changes. This notification is actually the occurrence of the DataChanged event. 4. When the main program needs to display a list of data included in a plugin, it calls the getData method. This method returns an array of iPlugData objects. Such a main program can be aligned Each object uses the toString method to get the data to create each node of the tree. TOSTRING method is an overload of the Employeedata class for displaying the name of the employee. 5.iplug interface also defines the Save and Print methods. Definition these two methods The purpose is to inform a plug-in when there is a need to print or save data. EmployeEPlug class is the function of implementing printing and saving data. When using the Save method, the location where the data needs to be saved will be called. It is provided here. It is assumed here to query the user's query path. Path information is a service providing to each plugin. For the Print method, the main program will pass the options and content to system.drawing.printing.printDocument class Example. In both cases, and the user's interaction is consistently provided by the main program.
Reflection]
After a plug-in is defined, the next step is to view the main program how to load the plug-in. In order to achieve this goal, the main program uses the reflex mechanism. Reflection is the .NET for runtime View Type information. In reflection With the help of the mechanism, the type information will be loaded and viewed. This can be checked by checking this type to determine if the plugin is valid. If the type passes the check, the plugin can be added to the main program, it can be operated by the user .
The sample program uses the three built-in classes of the .NET framework to use reflection: System.Reflection.Assembly, System.Type, and System.Activator.
The System.Reflection.Assembly class describes the .NET's assembly. In .NET, the assembly is a configuration unit. For a typical Windows program, the assembly is configured as a single Win32 executable, and has specific additional additional Information, make it adapt to the .NET running environment. The assembly can also be configured as a Win32 DLL (Dynamic Link Library), which also requires additional information required for .NET. System.Reflection.Assembly class can get the assembly when running Information. This information includes the type information contained in the assembly. Thestem.Type class describes the type definition. A type declaration can be a class, interface, array, structure, or enumeration. After loading a class, SYSTEM. The Type class can be used to enumerate the method, attribute, event, and interface supported by this class.
The System.Activator class is used to create an instance of a class.
[Loading plugin]
Listing 4 shows the loadPlugs method .LoadPlugs method is defined in HostForm.cs, is a non-static method of a private of the Hostform class. LoadPlugs method uses .NET reflex mechanism to load the available plugin files and verify whether they meet the main program Use the request, then add them to the tree display area of the main program. This method contains the following steps:
1. By using the System.Directory class, our code can use wildcards to find all files that are .plug as extension files. And the Directory class's static method GetFiles can return a system.string type array to get every The physical path of the required document. 2. After obtaining the path string array, you can start loading the file into the System.Reflection.Assembly instance. Create the code of the asDSembly object uses the TRY / CATCH code block, so if One file is not an effective .NET assembly, it will throw an exception, and a MESSAGEBOX dialog will be popped up, telling users that the file cannot be loaded. The loop has been running until all files have been traversed. 3. After an assembly load, the code will traverse all accessible type information, check if the hostCommon.iPlug interface is supported. 4. If all types support the HostCommon.iplug interface, then the code continues to verify these types, check whether you have supported those Pre-plug-in defined properties. If no support, the exception of a hostCommon.plugnotvalIDException type will be thrown, the main program will pop up a MessageBox, tell the user's specific information. Loop has been running until all files It is traversed. 5. Finally, if these types support the hostCommon.iPLUG interface, all the properties that need to be defined have also been defined, then it will be packaged as a PlugTreenode instance. This instance will be added to the tree display of the main program. Area.
[achieve]
The main program frame is designed to two assemblies. The first assembly is host.exe, which provides the main program's Windows Form interface. The second assembly is HostCommon.dll, which provides the main program and plugin All types of customs needed to communicate. For example, the iPlug interface is configured in HostCommon.dll so that it can be accessed by the main program and plug-in. These two assessments are in a folder, the same, Additional assemblies as a plug-in also need to be configured together. These assemblies are configured in the PLUGS folder (a subfolder of the main program directory) .EmployeEPlug class is defined in the Employee.plug program, and the CustomerPlug class is Customer.Plug program set. This example specifies that the plugin file is extension in .plug as an extension. In fact, these plugins are a normal .NET library file, just usually library files, here, here. Plug. Special extensions have no effect at all for programs, but it allows users to know more and know this is a plugin file.
[Comparison]
It is not necessary to design it like an example program. For example, when developing a C # program with plug-ins, it is not necessarily usable. The example uses two custom properties, but it can also be newly defined. The parameters of the two IPLUG interfaces are implemented. The properties are selected here because the names of the plug-in and its description are essentially the properties of a thing, in line with specifications. Of course, use properties will cause main programs to need more Reflected code. For different needs, designers always need to make a reasonable decision.
[to sum up]
The sample program is designed to be as simple as possible to help understand the communication between the main program and the plugin. When actually products, you can do a lot of improvements to meet practical requirements. For example::
1. Adding more methods, attributes, events, increases communication points between main programs and plugins, more interactive operations between the two, make the plug-ins for more things. 2. Can be allowed The user actively selects the plugin that needs to be loaded.
[Source Code] The complete source code of the sample program can be downloaded here. FTP: //ftp.cuj.com/pub/2003/2101/walchessk.zip
[Remarks] [1] Erich Gamma et al. Design Patterns (Addison-Wesley, 1995).
Picture 1:
List 1: The iPlug Interface
Public interface iplug {iplugdata [] getData (); PlugDataEditControl getEditControl (IPLUGDATA DATA); BOOL Save (string path); Bool Print (PrintDocument Document)
Listing: The PlugDisplayNameAttribute Class Definition
[AttributeUSAGETS.CLASS] PUBLIC CLASS PLUGDISPLAYNAMEATTRIBUTE: System.attribute {private string _displayName
Public PlugDisplayNameAttribute (String DisplayName): base () {_displayname = displayName; return;
Public override string toString () {return _displayName;
List three: a Partial Listing of the EmployeePlug Class Definition
[PlugDisplayName ( "Employees")] [PlugDescription ( "This plug is for managing employee data")] public class EmployeePlug: System.Object, IPlug {public IPlugData [] GetData () {IPlugData [] data = new EmployeeData [] { New Employeedata ("Jerry", "Seinfeld"), New Employeedata ("Bill", "COSBY"), New Employeedata ("Martin", "Lawrence")}
Return Data;}
Public PlugDataEditControl GetitControl (iPlugData Data) {Return New Employeecontrol (Employeedata) DATA);}
Public Bool Save (String Path) {// Implementation Not Shown}
Public Bool Print (PrintDocument Document) {// Implementation Not Shown}}}}
List 4: The Method LoadPlugs
Private void loadplugs () {string [] files = directory.getfiles ("plugs", "* .plug");
Foreach (string f in files) {try {askFROMBLY.LOADFROM (f); system.type [] types = a.gettypes (); foreach (system.type type in type) {if (type.getinterface) {g (" IPlug "!) = null) {if (type.GetCustomAttributes (typeof (PlugDisplayNameAttribute), false) .Length = 1) throw new PlugNotValidException (type,!" PlugDisplayNameAttribute is not supported "); if (type.GetCustomAttributes (typeof (PlugDescriptionAttribute ), False) .length! = 1) throw new plugnotvalidexception (Type, "PlugDescriptionATTRIBUTE IS Not Supported);
_tree.nodes.add (new plugtreenode (type));}}} catch (exception e) {messagebox.show (e.Message);}}
Return;}
Shawn Patrick Walcheeske is a software development engineer in Phoenix, Arizona, USA. He is also Microsoft Certified Solution Developer and Sun Certified Program for the Java 2 Platform. You can contact him here, Questions@walcheeske.com .
[Translator Note] In the past, how to implement plug-in mechanism in .NET, do it to do it, always feel that it is not good enough. And yesterday, I found this article in the Internet, and I was really great. So after reading it, I decided to translate it. I spent about 10 hours before and after. Translation may not be very good, please forgive me. If there is any mistake in the text, please don't finish correct. My E-mail: sunmast @ VIP. 163.com