First, the introduction
1. Introduction of the problem
Assume that the program you are designing is already deployed to the user's computer, and that it will function properly. But one day, the user called-they asked for new features to be added. Once you've identified the needs of your users, you've found that the original software architecture is not up to the requirements for new tasks-you need to redesign the application. But the problem is, even if you use a development cycle to complete the user needs of the application, but do not guarantee that the user's requirements will not change again. In other words, the possibility of the spread of demand still exists. Therefore, the plug-in architecture can show its superiority in this case.
2. Comparison of several solutions
I have summed up the framework of the plug-ins I have contacted, which can be divided into the following categories:
i>-Script Use a language to write the program logic of Plug-ins into scripting code. This language can be Python, or any other existing scripting language that has been tested by users for a long time. Even, you can design a scripting language to match the special needs of your program. Of course, using the most popular XML today is a perfect fit. The feature of this form is that users with a little bit of programming knowledge can modify your script (^_^ If you don't encrypt it). We can't argue whether it's good or bad. Because the consequences of this situation are unpredictable. ii> Dynamic Function Library DLL Plug-in functionality exists as a dynamic library function. The main program obtains the function signatures in the plug-in DLL through some channel (the plug-in writer or some tools), and then calls them where appropriate. Matlab readers know that the various functions of MATLAB are almost all dynamic linked to the function. Iii> Aggregation Type As the name suggests, is the plug-in function directly written as an EXE. The main program, in addition to completing its own duties, is responsible for scheduling these "Plug-ins". I don't like this form. This makes it much more difficult to communicate between the plug-in and the plug-in, and between the main program and the plug-in (mainly this). The failure of Babel [1] is to some extent the result of the inability of information exchange to be realized. Iv> COM Components The generation of COM [2] adds a bit of energy to the world. Only interfaces. What our plug-ins need to do is to implement the interface defined by the program. The main program does not need to know how the plug-in implements the predefined functionality, it only needs to access the plug-in through the interface and provides the interface of the main program-related object. As a result, the exchange of information between the main program and the plug-ins becomes extremely simple. Also, plug-ins are completely transparent to the main program. |
3. Decision Making
C # is an object-oriented programming language. It provides the interface keyword to directly define the interface. At the same time, the System.Reflection namespace also provides a series of related objects for accessing external assemblies. This is for us in C # to implement plug-in architecture has laid a solid foundation.
Below, we will illustrate the implementation of this architecture in C # with an example of a program editor with a plug-in architecture.
Second, the design process
OK, now we're going to put all the core code in the Cspluginkernel namespace. Build a C # class library project with Vside. Start our code in the namespace Cspluginkernel.
1. Interface design
Our program editor will open the Document object that is being edited to the plugin. After the program starts, it enumerates each plug-in and connects it to the main program, passing the interface of the main program object. The plug-in can use this interface to request the main program object or access the main program functionality.
Depending on the requirements above, we first need a main program interface:
Public interface Iapplicationobject { void Alert (string msg); Generate a message void Showinstatusbar (string msg); Displays the specified information in the status bar Idocumentobject querycurrentdocument (); Get the Document object that is currently in use Idocumentobject[] Querydocuments (); Get all the document objects Setting up event handlers void Setdelegate (Delegates Whichone, EventHandler Targer); } Only this one event is required Public enum Delegates { Delegate_activedocumentchanged, } |
Then the Idocumentobject interface. The plug-in accesses the editor object through this interface.
/// The editor object must implement this interface /// Public interface Idocumentobject { These properties are the corresponding property mappings for the RichTextBox control String Selectiontext {get; set;} Color SelectionColor {get; set;} Font SelectionFont {get; set;} int SelectionStart {get; set;} int SelectionLength {get; set;} String Selectionrtf {get; set;} BOOL HasChanges {get;} void Select (int start, int length); void AppendText (String str); void SaveFile (string fileName); void SaveFile (); void OpenFile (string fileName); void CloseFile (); } |
This interface does not require much explanation. Here I only implemented a few of the RichTextBox control methods, the other may be used, the reader can add themselves.
Then, according to the plug-in's behavior in its lifecycle, design the plug-in interface.
/// The plugin for this program must implement this interface /// Public interface IPlugin { Connectionresult Connect (Iapplicationobject app); void Ondestory (); void OnLoad (); void Run (); } /// Represents the result of a plug-in connecting to the main program /// public enum Connectionresult { Connection_success, Connection_failed } |
The main program first calls the Connect () method and passes iapplicationobject to the plugin. The plugin does some initialization work in this process. The OnLoad () method of the plug-in is then invoked. After that, when the main program receives a signal to invoke the plug-in (the keyboard, mouse responds), the plug-in's Run () method is invoked to start the plug-in. At the end of the program, call its Ondestory () method. In this way, the life of Plug-ins to end.
2. Storage and acquisition of plug-in information
A plug-in needs to have its name, version, and other information. As a designer, you must also leave your name and personal website to advertise yourself. The new features of C #-attributes-are a good solution. So we define a class Plugininfoarrtibute that inherits from System.Attribute:
/// Used to specify information about a plug-in /// public class PluginInfoAttribute:System.Attribute { /// Deprecated. Don't use. /// Public Plugininfoattribute () {} Public Plugininfoattribute ( String name, string version, String author, string webpage, bool Loadwhenstart) { Details have been omitted } public string Name {get {_name}} public string Version {get {_version;}} public string Author {get {return _author;}} public string Webpage {get {return _webpage;}} public bool Loadwhenstart {get {return _loadwhenstart;}} /// Used to store some useful information. /// public Object Tag { get {return _tag;} set {_tag = value;} } /// Used to store serial numbers /// public int Index { get {return _index;} set {_index = value;} } private string _name = ""; private string _version = ""; private string _author = ""; private string _webpage = ""; Private object _tag = null; private int _index = 0; Not for the time being. private bool _loadwhenstart = true; } |
Use this class to modify your plugin and let him implement the IPlugin interface:
/// My pluging 1 (Just for test) /// [ Plugininfo ( "My pluging 1 (Just for test)", "1.0", "Jack H Hansen", "http://blog.csdn.net/matrix2003b", True) ] public class Myplugin1:iplugin { Public MyPlugin1 () {} #region IPlugin Members Details have been omitted #endregion Private Iapplicationobject _app; Private Idocumentobject _curdoc; } |
3. Load Plugin
Now we have to use the System.refelction namespace. The program searches for each file in the plugins directory at startup. For each file, if it is a plug-in, load it with the Assembly object. Each object in the assembly is then enumerated. The way to judge whether an assembly is our plug-in is to determine whether it is implemented directly or indirectly from IPlugin. Use the following function to pass the System.Type of an object enumerated from an assembly.
private bool Isvalidplugin (Type t) { BOOL ret = FALSE; Type[] interfaces = T.getinterfaces (); foreach (Type theinterface in interfaces) { if (Theinterface.fullname = = "Cspluginkernel.iplugin") { ret = true; break; } } return ret; } |
If all conditions are met, Isvalidplugin () returns True. The program then creates the object and saves it in a ArrayList.
Plugins. ADD (Pluginassembly.createinstance (plugingtype.fullname)); |
Now, you can write the test code.
Third, the source code
Due to space limitations, complete source code (including test cases) can be downloaded from the link below. After downloading, open with vs.net2003 and rebuild the solution (requires the. NET Framework 1.1). A test case is a plug-in that inserts red text into a RichTextBox control. Very simple, only for testing purposes.
Four, the conclusion
That ' s all! With this plug-in architecture, poor program members will no longer need to spread the cost of the need for the needs of the scheming. In addition, you are welcome to comment on this article and the additional code in this article. Also, is, often to my Blog to see ~ ~ ^_^
Note: [1] Babel's failure the human moon myth, Frederick p. Brooks Jr. 7th Chapter Why Babel will fail [2] com for detailed technical details about com/com+ see Mastering com and COM +, Ash rofail, Yasser Shohoud. |