Http://www.cnblogs.com/wayfarer/articles/28537.html
I. Introduction
1. Introduction of the problem
Assume that the program you designed has been deployed on your computer and runs properly. But one day, the user called and asked for new features. After determining the user's requirements, you can find that the original software architecture is no longer qualified for new tasks-you need to redesign this application! But the problem is that even if you use another development cycle to complete the applications required by the user, there is no guarantee that the user's requirements will not change again. That is to say, the possibility of spreading demand still exists. Therefore, in this case, the plug-in architecture can better demonstrate its superiority.
2. Comparison of several solutions
I have summarized the plug-in architecture I have come into use, which can be roughly divided into the following categories:
I> script Write the program logic of the plug-in into script code in a certain language. This language can be Python or other existing script languages that have been tested by users for a long time. You can even design a scripting language to meet your special needs. Of course, using the most popular XML today is no longer appropriate. This form features that users with a little programming knowledge can modify your script on their own (pai_^ if you do not encrypt it ). We cannot argue whether this is good or bad. Because the consequences of this situation are unpredictable. II> dynamic function library DLL Plug-in functions exist in the form of dynamic library functions. The main program uses some channel (plug-in writer or some tools) to obtain the function signature in the plug-in DLL, and then calls them in the appropriate place. Readers who have used MATLAB know that almost all functions of Matlab are dynamic chained functions. III> Aggregation As the name implies, the plug-in function is directly written as exe. In addition to fulfilling your responsibilities, the main program is also responsible for scheduling these "ins ". I don't like this form. This makes it much more difficult to exchange information between plug-ins and between the main program and plug-ins. Babylon's failure[1] To some extent, information exchange cannot be achieved. IV> COM components Com[2]The emergence of the world has added a bit of vitality. Only interfaces are available! All we need to do is implement the interface defined by the program. The main program does not need to know how the plug-in can implement predefined functions. It only needs to access the plug-in through interfaces and provide interfaces for the objects related to the main program. In this way, information exchange between the main program and various plug-ins becomes very simple. In addition, the plug-in is completely transparent to the main program. |
3. Decision-making
C # is an object-oriented programming language. It provides interface keywords to directly define interfaces. At the same time, the system. Reflection namespace also provides a series of related objects to access the external assembly. This laid a solid foundation for implementing the plug-in Architecture in C.
Next, we will take a program editor with a plug-in architecture as an example to illustrate the implementation of this architecture in C.
Ii. Design Process
Now we are ready to put all the core code in the cspluginkernel namespace. Use vside to create a C # class library project. Start our code in the namespace cspluginkernel.
1. Interface Design
Our program editor will open the document objects being edited to the plug-in. After the program starts, enumerate each plug-in, connect it to the main program, and pass 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 function
.
Based on the above requirements, we first need a main program interface:
Public interface iapplicationobject { Void alert (string MSG); // generates a message Void showinstatusbar (string MSG); // display the specified information in the status bar Idocumentobject querycurrentdocument (); // obtain the currently used Document Object Idocumentobject [] querydocuments (); // obtain all document objects // Set the event Processor Void setdelegate (delegates whichone, eventhandler targer ); } // Currently, only this 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 attributes are the corresponding property ing of 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 need to be explained too much. Here I only implement a few methods of the RichTextBox Control. For other methods that may be used, add them as needed.
Then, design the interface of the plug-in based on the behavior of the plug-in its lifecycle.
/// /// The plug-in of this program must implement this interface /// Public interface iplugin { Connectionresult connect (iapplicationobject APP ); Void ondestory (); Void onload (); Void run (); } /// /// Indicates the connection result between the plug-in and the main program. /// Public Enum connectionresult { Connection_success, Connection_failed } |
The main program will first call the connect () method and pass the iapplicationobject to the plug-in. The plug-in performs initialization in this process. Then, the onload () method of the plug-in is called. After that, when the main program receives a signal to call the plug-in (keyboard, mouse response), it will call the plug-in's run () method to start the plug-in. The ondestory () method is called when the program ends. In this way, the life of the plug-in ends.
2. Storage and acquisition of plug-in information
A plug-in must have its name, version, and other information. As a designer, you must also leave your distinguished name and personal website to promote yourself. C #'s new feature-attribute is a good solution. Therefore, we define a class plugininfoarrti.pdf inherited from system. attribute:
/// /// Specify information about a plug-in /// Public class plugininfoattribute: system. Attribute { /// /// Deprecated. Do not use. /// Public plugininfoattribute (){} Public plugininfoattribute ( String name, string version, String author, string webpage, bool loadwhenstart ){ // The details are omitted. } Public string name {get {return _ name ;}} Public String version {get {return _ 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; // Temporarily unavailable Private bool _ loadwhenstart = true; } |
Use this class to modify your plug-in and enable it to 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 Member // The details are omitted. # Endregion Private iapplicationobject _ app; Private idocumentobject _ curdoc; } |
3. Load the plug-in
Now we need to use the system. refelction namespace. When the program starts, it searches for every file in the Plugins directory. For each file, if it is a plug-in, it is loaded with an assembly object. Then enumerate every object in the Assembly. The method to determine whether an assembly is our plug-in is to determine whether it directly or indirectly implements self-iplugin. Use the following function to pass the system. Type of the object enumerated from the 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 the conditions are met, isvalidplugin () returns true. Then the program creates this object and stores it in an arraylist.
Plugins. Add (pluginassembly. createinstance (plugingtype. fullname )); |
Now you can write the test code.
Iii. Source Code
Due to space limitations, please download the complete source code (including test cases) at the following link. After downloading the file, use vs. net2003 to open the file and regenerate the solution (. NET Framework 1.1 is required ). A test case is a plug-in that inserts red text into the RichTextBox Control. It is very simple and only used for testing.
Iv. Conclusion
That's all! With this plug-in architecture, poor programmers no longer have to worry about the spread of demand. In addition, you are welcome to evaluate this article and the additional code in this article. Also, visit my blog ~~ Pai_^
Note: [1] Tower of Babylon's failure: Mythical man-month, Frederick P. Brooks Jr.Chapter 4 why the tower of Babylon fails [2] For details about COM/COM +, see mastering COM and COM +.Ash rofail, Yasser shohoud. |