C # Plugin Architecture combat
C # Plugin Architecture in combat Jack H Hansen [2004-07-27]
Keywords C # Plug-in Reflection (System.Reflection) property (System.Attribute)
First, Introduction
1. Introduction of the problem
Let's say you've designed a program that's already deployed to the user's computer and that it works. But one day, the users called-they asked for new features to be added. Once you've identified the user's needs, you've found that the original software architecture is not up to the requirements of the new task-you need to redesign the app! But the problem is, even if you use a development cycle to complete the application users need, there is no guarantee that the user's needs will not be changed again. In other words, the likelihood of a demand spread is still there. Therefore, the plug-in architecture can show its superiority in this case.
2. Comparison of several solutions
I have summed up the plug-in architecture I have come into contact with, which can be broadly divided into the following categories:
i> Script Style
Use a language to write the program logic of the plugin into script code. This language can be Python, or other existing scripting languages that have been tested by the user for a long time. You can even design a scripting language to match the special needs of your program. Of course, using the most popular XML today is just the right thing to do.
The feature of this form is that a user with a little bit of programming knowledge can modify your script (^_^ If you don't encrypt it). We cannot argue whether this is a good or a bad thing. Because the consequences of this situation are unpredictable.
ii> Dynamic Function Library DLL
Plug-in functionality exists in the form of dynamic library functions. 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 in the appropriate place. The readers who have used Matlab know that the functions of MATLAB are almost all functions of dynamic link.
Iii> polymerization Type
As the name implies, is the plug-in function directly written EXE. The main program is responsible for dispatching these "plugins" in addition to fulfilling its responsibilities. I don't like this form. This makes it much more difficult to communicate between the plug-in and the plug-in, between the main program and the plug-in (mostly this). The failure of Babel [1] is to some extent the inability of information exchange to be achieved.
Iv> COM Components
The generation of COM [2] Adds a bit of vitality to the world. Only Interface! What our plug-ins need to do is implement a program-defined interface. The main program does not need to know how the plug-in implements the predetermined function, it only needs to access the plug-in through the interface, and provides the interface of the main program related objects. In this way, the communication between the main program and the plug-ins becomes extremely simple. Also, the plug-in is 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. Also, the System.Reflection namespace provides a series of related objects that access external assemblies. This lays a solid foundation for us to implement the plugin architecture in C #.
Below, we will take an example of a program editor with a plugin architecture to illustrate the implementation of this architecture in C #.
Second, the design process
Okay, 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 opens the Document object 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 request the main program object or access the main program function through this interface.
Based on the above requirements, 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 (); Gets the currently used document object
Idocumentobject[] Querydocuments (); Get all the document objects
Setting up event handlers
void Setdelegate (Delegates whichone, EventHandler Targer);
}
This is the only event that needs to be
public enum Delegates {
Delegate_activedocumentchanged,
}
Then the Idocumentobject interface. The plugin 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 need to be interpreted too much. Here I only implemented a few of the RichTextBox control methods, others may be used, the reader can add themselves.
Then, based on the behavior of the plugin in its life cycle, design the interface of the plug-in.
///
The plugin of 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 connection to the main program
///
public enum Connectionresult {
Connection_success,
Connection_failed
}
The main program calls the Connect () method first and passes the Iapplicationobject to the plugin. The plugin does some initialization work in this process. The plugin's OnLoad () method is then called. After that, the plugin's Run () method is invoked to launch the plugin when the main program receives a signal from the plug-in (keyboard, mouse response). Call its ondestory () method at the end of the program. In this way, the plugin's life is declared to be over.
2. Storage and acquisition of plugin information
A plugin 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 feature of C #--attributes, is 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. 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;}}
///
Used to store some useful information.
///
public Object Tag {
get {return _tag;}
set {_tag = value;}
}
///
Used to store ordinal 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 moment.
private bool _loadwhenstart = true;
}
Use this class to decorate 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. Loading plugins
Now you 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 plugin, load it with the Assembly object. Then enumerate each object in the assembly. The way to determine whether an assembly is our plugin is to determine whether it is implemented directly or indirectly from IPlugin. Use the following function to pass the System.Type of an object from an assembly enumeration.
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 () 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) should be downloaded in the link below. After downloading, open with vs.net2003 and rebuild the solution (requires. NET Framework 1.1). A test case is a plug-in that inserts red text into a RichTextBox control. Very simple, just for testing purposes.
Iv. Conclusion
That ' s all! With this plug-in architecture, poor handlers no longer have to spend a lot of time on the need to spread. Also, you are welcome to comment on this article and the additional code in this article. Also, is, often go to my Blog to see ~ ~ ^_^
Note:
[1] Babel's failure "People's Month myth", Frederick P. Brooks Jr 7th Chapter Why Babel will fail
[2] For detailed technical details about com/com+, see "Mastering com and COM +", Ash Rofail, Yasser Shohoud.
Visual C # Plugin Architecture Combat Addendum
In the process of software development, the process of design is often more difficult than the process of writing code. Therefore, in addition to software testing, often the most time-consuming is the system modeling. A good software system should have high stability (reliability), ease of operation, and scalability support, especially scalability. I think that good scalability support is a necessary condition for a software team to become active in development. For an application, we want to be able to solve the problem with the least amount of time and effort when the user increases demand. When others are busy with the fast-growing demands of the user (the user is not always able to tell you exactly what they need to do during the first demand analysis), and you, your team just need to do a little work to get "insatiable" users to be satisfied, thereby improving efficiency, so that the team has more time to create, Instead of making unnecessary changes.
Unfortunately, I did not take this into account in the article "C # Plugin Architecture combat". Of course, for a 18-year-old young man who has no or no team work experience, such lapses (mistakes are failures – the teacher says) are excusable (self-justification). However, I decided to refactor the plugin system.
Taking into account the complexity of the system, I am going to use UML this time (I learned it only last month, and I didn't draw well, laughed at).
1. Proceed to analysis
For the netizen of the advice, I probably understand, but people's thinking is too big, I do not dare to ensure that my understanding is fully in line with the meaning of Jan. However, I will still build an application framework model based on my own understanding of extensibility.
Straight into the chase. I now assume that I belong to a software team (just call her abstractsoft) and be a system analyst. Everything has its normative side, we want our team to produce all the applications deployed on the same platform have the same framework, the same deployment form. This makes it possible to create unique team features and to compete with efficiency to win. Because we don't need to design different frameworks for each application--This can save a lot of time!
This way I need to separate the program implementation from the user interface into different frameworks. I mean:
As a result, there are abstract interfaces and some generalization details in the core library of application Frame level. This content was deployed on the user's machine the first time the team product was installed. It is not automatically destroyed until the user submits a request to remove it from the local. GUI level provides a unified interface component (such as a property editor, a database operator interface, and other reusable components) after the team's product generalization. The special product (speciallized application) implements the extensibility by implementing some interfaces in the application Frame level by using classes in the GUI level to implement the user interface.
The following is a simple static diagram (the interfaces and members of the class are elaborated below):
2. Iconnectableobject
Public interface Iconnectable {
Application is the main frame object to which the plug-in belongs. Null indicates that the plug-in itself is the main frame
Connectionresult Connect (object application);
Extendibleversioninfo versioninfo {get;}
void Ondestory ();
void OnLoad ();
void Run ();
}
public enum Connectionresult {
Connection_success,
Connection_failed
}
public class Extendibleversioninfo {
Private Extendibleversioninfo () {}
Public Extendibleversioninfo (string name, string version, string copyright) {//omitted}
Public Extendibleversioninfo (String name,int version1,int version2,int version3,string Copyright) {//omitted}
public int Primaryversion {get {return _version1;}}
public int Secondaryversion {get {return _version2;}}
public int Buildversion {get {return _version3;}}
public string Name {get {return _name;}}
public string VersionString {get {//omitted}}
public string Copyright {get {return _copyright;}}
private string _name;
private int _version1 = 1;
private int _version2 = 0;
private int _version3 = 0;
private string _copyright;
public static Extendibleversioninfo Empty = new Extendibleversioninfo ();
}
All the objects that can be connected must implement this interface. This is the originator of classes in all application Frame level.
3. iextendible
Public interface Iextendible {
Iconnectable getlatestversion ();
Iconnectable queryspecifiedversion (extendibleversioninfo version);
Extendibleversioninfo[] Enumerateversions ();
}
4. Use the class factory to create the latest version of the application and plug-in
Our main program and plugins are designed to be internal class. The program only outputs one factory class, and the user interface obtains these instances of the objects used to complete the actual task by invoking the Getlatestversion () method of the Iextendible interface and displays them. Alternatively, you can enumerate all the versions, allowing the user to select the desired version.
5. Extensibility
Admittedly, the scalability of this approach is still not very strong. When a program needs to be upgraded, it needs to modify the factory class provided to the user (although the interface is unchanged). For better scalability, the simple factory model can be converted to a factory method pattern.
C # plug-in architecture Combat + Visual C # Plugin Architecture combat Addendum (GO)