. NET responsibility chain model, Singleton mode, template method mode mix
Preface
Wow, see the topic is very long, this combination of things, in the end what? This article to ponder together, these two days for the team's software to rush, I am responsible for the piece called: Plug-in Manager. Our team members of the language is quite scattered, I C #, Captain VB.net, there are other members write easy language, etc., the system function plug-in is we separate write, each with their favorite language to write each function module plug-in, and finally with my development of the plug-in manager to integrate all the plug-ins together. This makes me a headache ah, a C # version of the plug-in, a vb.net version of the plug-in, a simple language plug -In, if there are new members to join, and a python version of the plugin, call me how to be good. The most common and rotten way to do this is to write many versions of the reader, and then use if to read the information according to the plug-in language to the reader that should be the version, Wow, if it comes in more than 10 languages, it would be a pit-daddy. One might say: Introducing KERNEL32 can solve the non-. NET language plug-in reading it. It is true that the scheme is currently used and can be easily spoken. NET language uses. NET plug-in DLL reads, but is not sure Kernel32 can solve any language plug-ins. So in order to let myself have a posterior, I combined with the topic of the three design patterns, wrote a template, the following look at this template can do, why to use this. If there is a place to use inappropriate, I hope that the friends of the Beat, I will study hard to correct, if you feel can, point a recommendation, thank you ~
Framework Presentation and description
Let's look at the class diagram below and describe the responsibilities of each class separately. The plugin class is a data structure class for plug-ins, and Plugintype is an enumeration that defines the types of all plug-ins, such as Dotnet,python, which need to be modified when extended. Then the rest is the focus of this chapter, the user code client uses the Ipluginanalyzerable interface to read the plug-in information, the interface has two implementation classes, one is Pluginanalyzer (abstract class, the public part of the reader to define the various languages), This class implements the template method pattern and the responsibility chain pattern, so that the processing command can shirk responsibility among its subclasses, and its subclass currently has two, which is the specific reader of Dotnet,python. The last class, Componentanalyzer, acts as a wrapper, initializes the chain of responsibility, implements the singleton pattern, and provides a call to the user code. So, the final effect is: Get the instance of Componentanalyzer, call one of the methods, the method calls the specific reader chain, finally who corresponding and processing the method call user is not aware of, probably the idea is this.
Implementing a process
This implementation process is only the implementation of the template, the specific application to the project also needs to make corresponding changes.
Without further ado, we implement the plugin class and the Plugtype enumeration first:
Plugin:
class Plugin { Plugin (Plugintype type,string pluginpath) {
type;
Pluginpath; }
public plugintype belongtype {
set ;
get
public String Pluginpath {
set ;
get
Plugtype:
enum Plugintype { DotNet=0, Python=1, }
After implementing the two basic types, and then looking at the plays in this article, I'll attach the code side to the instructions that help understand. Start with the ipluginanalyzerable of the highest layer first:
Interface ipluginanalyzerable { void Analyze (Plugin Plugin); }
User code is to use the interface's Analyze method to handle incoming plug-ins, and then the next layer has two classes, one is abstract class Pluginanalyzer, one is Componentanalyzer.
Abstract classpluginanalyzer:ipluginanalyzerable {protectedPlugintype Analyzertype; Public voidAnalyze (Plugin Plugin) {if(plugin. Belongtype = = This. Analyzertype) {String author=Getauthor (plugin); String version=getversion (plugin); Console.WriteLine (String.Format ("\ r \ n analyst: {0}, plug-in type: {1} \r\n{2}\r\n{3}", This. GetType (). Name,plugin. Belongtype,author, version)); } Else { if(Nextanalyzer! =NULL) {nextanalyzer.analyze (plugin); } } } PrivatePluginanalyzer Nextanalyzer; PublicPluginanalyzer Nextanalyzer {Set { This. Nextanalyzer =value; } Get { return This. Nextanalyzer; } } protected AbstractString Getauthor (Plugin Plugin); protected AbstractString getversion (Plugin Plugin); }
Interpretation:
Every specific class that inherits this class has two fields
1: The owning type (Plugintype enumeration), the variable name is Analyzertype.
2: The next analyst (Pluginanalyzer), which is the sibling class (likewise inheriting Pluginanalyzer).
Each specific class that inherits this class needs to rewrite the two methods Getauthor and GetVersion, which will be used within the Template Method Analyzer.
Template Method Analyzer first determines whether the type of plug-in is passed in the same type as it can handle, if the same is called its own method of processing, if the difference is to give the processing right to their next analyst. This completes the construction of the concrete structure.
Then there are the specific reader classes, each with their own way of handling the same task. All Inherit pluginanalyzer
Dotnetpluginanalyzer (this class is handled.) NET plug-in reader)
classDotnetpluginanalyzer:pluginanalyzer { PublicDotnetpluginanalyzer () {Base. Analyzertype =plugintype.dotnet; } protected Override stringGetauthor (Plugin Plugin) {return "dotnet Plug-in, author name: Jarvin"; } protected Override stringgetversion (Plugin Plugin) {return "dotnet Plug-in, version number:!!! v2014!!"; } }
Pythonpluginanalyzer (This class is the reader that handles Python plugins)
classPythonpluginanalyzer:pluginanalyzer { PublicPythonpluginanalyzer () { This. Analyzertype =Plugintype.python; } protected Override stringGetauthor (Plugin Plugin) {return "python plugin, author name: Joker"; } protected Override stringgetversion (Plugin Plugin) {return "python plug-in with version number: V---strange----"; } }
OK, how to use it? I'll create a new class, wrap these readers up, and form a chain that provides a unified interface for user code calls, and see how I wrap it.
Componentanalyzer
classcomponentanalyzer:ipluginanalyzerable {PrivateComponentanalyzer () {Rootanalyzer=NewDotnetpluginanalyzer (); Pythonpluginanalyzer Pythonanalyzer=NewPythonpluginanalyzer (); Rootanalyzer.nextanalyzer=Pythonanalyzer; } #regionSingle-Instance mode implementation Public StaticComponentanalyzer getinstance () {returnsinglehelper.getinstance (); } Private classSinglehelper {Private StaticComponentanalyzer me =NewComponentanalyzer (); Public StaticComponentanalyzer getinstance () {returnme; } } #endregionPluginanalyzer Rootanalyzer; Public voidAnalyze (Plugin Plugin) {rootanalyzer.analyze (Plugin); } }
A very simple class, we first look at the constructor: to connect all the language reader, and then the chain header is Rootanalyzer, we call the Analyze method every time we invoke the Rootanalyzer corresponding method, let it be passed internally. Get here, almost finished, you can download the source directly at the bottom of the run to see the results, the following gives the client test class.
classPrograme { Public Static voidMain (string[] args) {ipluginanalyzerable Dotnetanalyzer=componentanalyzer.getinstance (); Console.WriteLine ("Enter Q to exit"); while(true) {Plugin Plugin=Randomplugin (); Dotnetanalyzer.analyze (plugin); if(Console.ReadLine (). ToUpper () = ="Q") { Break; } } } Private StaticPlugin Randomplugin () {random random=NewRandom (); Plugintype type= (plugintype) random. Next (0,2); String Plaginpath=Path.getrandomfilename (); Plugin result=NewPlugin (type, plaginpath); returnresult; } }
View Code
test results: Look, execute in a consistent manner, but will get a different effect, the responsibility is pushed to the appropriate place to make corresponding treatment.
Someone here will say, how does that prove the extensibility of the model?? OK, now I expand a language reader, see how much I have changed, how much impact on the system?
Extensibility Testing
I take ruby for example.
1. Add an enumerated content in Plugintype Ruby:.
2. Add a ruby reader:
3. In the Componentanalyzer (previously said wrapper), add the reader to the chain!
In order to test, in the client code to modify the random generation number, so that it can generate 3, done! (This is a test-related, we did not modify the actual client any code)
Smooth expansion!!! We are only modifying the upper layer of code, for the underlying, also provides extension points, in line with the modification of the closed, open to the extension.
Summarize
Completed, we are tired, I hope there is no place for everyone to shoot, for the combination of programming, interface programming, do not target the implementation of class programming, this is my learning design patterns feel the deepest sentence. Thank you all for watching. Complete source code is provided below.
Full demo Download