Use .NET's Reflection to enhance the scalability of the object factory

zhaozj2021-02-17  88

Object factory

Object Factory mode is often used to generate an object from a derived system and return it as an instance of the base class, thereby obtaining the interface of the base class, and minimizes the details of the school to make full utilization The polymorphism of the object is a powerful function. Typically, the implementation method of the object factory is that in a factory method, first use a Switch statement based on type tag (TYPE TAG) to find the appropriate type, then create the instance of this type and return it.

For example, an illustration of a graphics system, which includes elements such as lines, circles, rectangles, which have some public operations, such as DRAW, Resize, and the like. Then we may have the following inheritance system:

In order to distinguish these classes in the object factory, we also need to specify a type tag separately. These types of tags can be enumeration, integer, string, and the like to uniquely mark the values ​​of these classes. Use their class name string as a marker looks good. We can use the object factory below to create Shape objects:

Public Sealed Class ShapeFactory

{

Private shapefactory ()

{

}

Public Static Baseshape Createshape (String ShapeID)

{

Switch (ShapeID)

{

Case "Rectangle":

Return new Rectangle ();

Case "circle":

Return new circle ();

Case "line":

Return new line ();

DEFAULT:

Return NULL;

}

}

}

The only use of ShapeFacory is to create a Shape instance, we do not want it to be inherited, or can be instantiated, so it is declared as Sealed, and has a private constructor. When we need to get an instance of a Shape, as long as you call ShapeFactory's createShape () method, it is incoming an appropriate ShapeID string, CreateShape () will return the correct Shape instance for us.

Enhanced extensibility

Now we have a Shape factory, it works well. But this factory has a significant shortcomings: it is difficult to expand. Whenever a SHAPE class is added to the system, we have to modify the createShape () method, join the new CASE statement. This is okay before our products are not released, we can fully control our code. However, when our product is released, users can easily derpt their own Shape classes from Baseshape, but they are hard to use the createShape () method to join their Shape class to the system because they cannot modify the createShape () method. achieve. So this Shape plant also needs some scalability. But a major obstacle to solving this problem is that if you don't use the switch statement, createShape () will not know which Shape classes exist in advance, and the relationship between type tags and specific classes.

Alexandrescu gives a C solution for this issue in his "Modern C Design". He uses a std :: map in the object factory class to maintain the relationship between type tag and type creation method, and add two interface register () and unregister () in the object factory class, used in this MAP Register or cancel the type tag and type creation method. For each of the addition of a Shape class, you need to write an anonymous namespace for this class, and call the register () method in this space to register your type tag and create methods into the object factory. In C #, we can learn from the Alexandrescu method, using a HashTable in the object factory to maintain the relationship between type tags and types, and use the register () method to register. Each Shape class must be responsible for its registration, so we add a registershape () method for each Shape class, which calls ShapeFactory.Register () to register yourself. But there are two questions:

1. When should the call to the registershape () method do you do?

2, C # does not support anonymous namespace, then how to call the registershape () method of each Shape class?

For the first question, we have a timing, that is, each Shape class must have been registered before the CreateShape () method is called. Complete the call to Registershape () in a static constructor is a good choice.

For the second question, we can use the Reflection mechanism, first traverse all types, find the type derived from the Baseshape, then call their registershape () method.

According to the above ideas, the implementation code of ShapeFactory is as follows:

Public Sealed Class ShapeFactory

{

Private static hashtable _creationmap = null;

Static ShapeFactory ()

{

_CREATIONMAP = new hashtable ();

AskEMBLY A = Type (ShapeFactory) .Module.Assembly;

Module [] Modules = a.getmodules ();

For (int i = 0; i

{

TYPE [] Types = modules [i] .gettypes ();

For (int J = 0; j

{

IF (! Types [J] .Equals (TypeOf (Baseshape))

&& Types [J] .BaseType! = NULL

&& Types [J] .BaseType.equals (TypeOf (Baseshape))))

{

Object obj = types [j] .invokemember (NULL,

BindingFlags.DeclaredOnly |

BindingFlags.Public |

BindingFlags.instance |

Bindingflags.createInstance,

NULL, NULL, NULL

Types [J] .invokememember ("Registershape",

BindingFlags.DeclaredOnly |

BindingFlags.Public | BindingFlags.nonpublic |

Bindingflags.instance | BindingFlags.InvokeMethod, NULL, OBJ, NULL;

}

}

}

}

Public Static Void Register (String ShapeID, Type Shape)

{

IF (! _ createMap.containskey (shapeID))

_CREATIONMap.Add (ShapeID, Shape);

}

Public Static Baseshape Createshape (String ShapeID)

{

TYPE Shape = (Type) _CREATIONMAP [shapeID];

IF (shape == null)

Return NULL;

Return (Baseshape) Shape.InvokeMember (NULL,

BindingFlags.DeclaredOnly |

BindingFlags.Public |

BindingFlags.instance | BindingFlags.createInstance,

NULL, NULL, NULL

}

}

The static constructor will be executed before the Createshape () method is called for the first time. It uses the Reflection mechanism to traverse all types, for the type derived from Baseshape, first create an instance, then call its regisershape () method to register it in HashTable. When the CreateShape () method is called, the corresponding type is removed from the HashTable in accordance with the incoming ShapeID and returns the real case. In this way, we have a Shape plant with certain scalability.

to sum up

Increase the REFLECTION mechanism in the object factory, enhance the scalability of the object factory to a certain extent. Improved ShapeFactory does not have to modify each time the Shape class, as long as the newly added Shape class implements the regisershape () method, it can be registered into the object factory and is created correctly. In this way, we can even easily implement plug-in functionality for our system. For example, we can specify a plug-in directory, traverse this directory, register the Shape class into the factory. Users only need to copy their plugins to this directory.

Of course, reflection may not be the best solution for this issue. It needs to traverse all types in the system, and the execution efficiency is not high. Fortunately, the static constructor will only be executed once. It is hoped that this article can play a role in tile jade. If you have a better solution, welcome to communicate with me. My contact information: sam1111@citiz.net

转载请注明原文地址:https://www.9cbs.com/read-31509.html

New Post(0)