I. Introduction
1. Introduction of the problem
Assuming that your design has been deployed to the user's computer and can run normally. But one day, users have called - they demand new features. After determining the user's needs, you have found that the original software architecture is unable to win the need for new tasks - you need to redesign this app! But the problem is, even if you use a development cycle to complete the user needs, it is not guaranteed that the user's demand will change again. In other words, the possibility of demand spread still exists. Therefore, in this case, the plug-in architecture can more show its superiority.
2. Comparison of several solutions
I summed up the plug-in architecture I have contacted, which can be roughly divided into the following categories:
I> script is written to script code using some language. This language can be Python, or other existing scripting languages that have been tested for a long time. Even, you can design a scripting language to cooperate with your program's special needs. Of course, use the most popular XML today. This form of features is that users who have a little programming knowledge can modify your script (^ _ ^ if you don't encrypt it). We can't argue this is the benefits or bad. Because the consequences of this situation are unpredictable. II> Dynamic Library DLL plug-in features exist in the form of dynamic library functions. The main program enables the function signature in the plugin DLL through a certain channel (plugin writer or some tool), and then calls them in the right place. Readers who have used Matlab know that all features in matlab are almost all dynamic link functions. III> A polymeric as the name suggests, is to write the plug-in function directly into EXE. In addition to completing your own responsibilities, the main program is also responsible for dispatching these "plugins". I don't like this form. This is difficult between the plug-in and the plugin, the main program and the plugin (mainly this) information exchange is much more difficult. The failure of Babylrenta [1] From a certain extent, information exchange cannot be achieved. IV> COM Component COM [2] adds a bit of vitality to this world. Only interface! Our plug-in needs to do just to implement the interface of the program definition. The main program does not need to know how the plugin implements a predetermined feature, which only needs to access the plugin through the interface and provide the interface of the main program related object. In this way, the information exchange between the main program and the plug-ins will become very simple. And, the plugin is completely transparent to the main program.
3. Decision
C # is an object-oriented programming language. It provides an interface keyword to directly define an interface. At the same time, the System.Reflection namespace also provides a series of related objects to access the external assembly. This has made a solid foundation for our implementation of the plug-in architecture in C #.
Below, we will explain the implementation of this architecture in C # as an example with a program editor with a plug-in architecture.
Second, the design process
Ok, now we are ready to put all the core code in the CSPLuginkerNel namespace. Create a C # class library project with VSIDE. Start our code in the namespace CSPLuginkernel.
Interface design
Our program editor will open the document object being edited to the plugin. After the program is started, you enumerate each plugin and connect it to the main program while passing the interface of the main program object. The plugin can request a main program object or access the main program function through this interface.
According to the above needs, we first need a main program interface:
public interface IApplicationObject {void Alert (string msg); // generates a message void ShowInStatusBar (string msg); // the specified information is displayed in the status bar IDocumentObject QueryCurrentDocument (); // get the document objects currently used IDocumentObject [] QueryDocuments (); // Get all document objects // Set the event processor void setdelegate (Delegates Whichone, EventHandler Targer);} // only only this event public enum delegates {DELEGATE_ACTIVEDOCUMENTCHANGED,} is then the iDocumentObject interface. The plugin accesses the editor object through this interface.
// // / Editor object must implement this interface /// (Public interface IDocumentObject {// These attributes are the corresponding attributes of the RichTextBox control map string selectiontext {get; set;} color selectionColor {Get; set;} font SelectionFont {GET; set;} int class;} int selectionLength {get; set;} string selectionrtf {get; set;} Bool Haschanges {Get;} void select (int start, int lay "; void appendtext (String Str); void savefile (string filename); void savefile (); void openfile; void closefile ();
This interface does not need too much explanation. Here I only realize a few ways of the RichtextBox control, others may be obtained, readers can add themselves.
Then, according to the behavior of the plugin in its lifecycle, the interface of the plugin is designed.
// // ////////////////////////////////////////////////////////////// Represents the result of the plugin and the main program /// public enum connectionResult {connection_suCcess, connection_failed}
The main course will first call the connect () method and pass the IapplicationObject to the plugin. The plugin does some initialization work in this process. Then, the onlOad () method of the plugin is called. After that, when the main program receives the signal (keyboard, mouse response), the plugin is called to start the plugin. At the end of the program, the onDestory () method is called. In this way, the life of the plugin is declared.
2. Storage and acquisition of plugin information
A plugin needs to have its name, version and other information. As the designer, you must also leave your name and personal website to promote yourself. The new feature-attribute of C # is a good solution. So we define a class pluginfoarrtibute: /// // used from System.attribute to specify 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) {// details have been 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;}} /// /// for storing Some useful information /// public object tag {get {return _tag;} set {_tag = value;}} /// // Used to store serial number /// 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 a temporary private bool _LoadWhenStart = true;}
Use this class to modify your plugin and let him realize an IPLUGIN interface:
///// My Pluging 1 /// [PluginInfo ("Just for Test", "1.0", "Jack H Hansen", "http://blog.9cbs. net / matrix2003b ", true)] public class MyPlugin1: IPlugin {public MyPlugin1 () {} #region IPlugin member // details have been omitted #endregion private IApplicationObject _App; private IDocumentObject _CurDoc;} 3 loading plug.
Now you have to use the System.refelction namespace. The program searches for each file in the plugins directory at boot. For each file, if it is a plugin, load it with an Assembly object. Then enumerate each object in the program. Judging whether an assembly is a way to make our plug-in is to determine if it is directly or indirectly implemented from iplugin. Use the following function to pass the system.type of the object from the program set enumerated.
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 the conditions are met, isvalidPlugin () will return 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.
Third, source code
Due to the limit, the complete source code (including test cases) is downloaded below. After downloading, please open it, regenerate the solution (requires .NET Framework 1.1). The test case is a plugin inserted into the red text in the RichTextBox control. Very simple, only test it.
Fourth, conclusion
That's all! With this plugin architecture, poor programmers do not have to spend the need for a demand. In addition, this paper is welcome to evaluate this article and the additional code herein. Also, that is, often go to my blog to see ~~ ^ _ ^
Note: [1] The failure of Babylrent Tower, "Moon Myth", Frederick P. Brooocks Jr. Chapter 7 Why Babylrenta Failed [2] COM For more technical details of COM / COM , please refer to "Mastering COM ", Ash Rofail, Yasser Shohoud.