One of the very useful design patterns when developing
large and flexible applications is the abstract factory.
Just a quick reminder of what an abstract factory is. The
definition is: Provide an interface for creating families of related or
dependent objects without specifying their concrete classes. So what does this
mean? Suppose your code has an order object and needs to generate an invoice. If
you are developing a flexible system that can handle changing the invoicing
system this would benefit from a factory. Instead of creating an invoicing
object directly you can use a factory to determine the correct invoicing object
to use.
There are a number of examples of how to do this but the
code typically looks something like:
Dim
generator As InvoiceGenerator
generator =
AbstactFactory.CreateObject(GetType(InvoiceGenerator))
or
generator =
AbstactFactory.CreateInvoiceGenerator()
Basically the object is not created through the New
operator but through a separate function. This works perfectly well but does
mean that you must decide in advance which objects are created through the
abstract factory and which are not because they are created in a different way.
However it is possible to use the New operator and still
retain the power of the abstract factory. The trick is in the combination of two
things. First subclass every class, that needs to be created using the abstract
factory, from the ContextBoundObject class. Secondly add a custom attribute to
these classes, in my case this is called AbstractFactoryAttribute.
So create the class like this:
<AbstractFactory()> _
Class
InvoiceGenerator
Inherits ContextBoundObject
...
End
Class
The AbstractFactoryAttribute inherits from ProxyAttribute
and needs to implement the CreateInstance() function to create the actual object
needed. The function GetClientObjectType() is the one that actually decides
which type of object is going to be created.
Imports
System.Reflection
Imports
System.Runtime.Remoting
Imports
System.Runtime.Remoting.Proxies
Class
AbstractFactoryAttribute
Inherits ProxyAttribute
Public Overrides
Function CreateInstance( _
ByVal
serverType As System.Type) As
MarshalByRefObject
Dim obj As
MarshalByRefObject
Dim realType As
Type
realType =
GetClientObjectType(serverType)
obj =
AllocateInitializedObject(realType)
Return obj
End Function
Public Shared
Function AllocateInitializedObject( _
ByVal
objectType As Type) As MarshalByRefObject
Dim obj As
MarshalByRefObject
Dim mi As
MethodInfo
mi =
GetType(RemotingServices).GetMethod("AllocateInitializedObject",
_
BindingFlags.Static Or BindingFlags.NonPublic,
_
Nothing, New
Type() {GetType(System.Type)},
Nothing)
obj =
CType(mi.Invoke(Nothing,
New Object() {objectType}),
MarshalByRefObject)
Return obj
End Function
Shared Function
GetClientObjectType(ByVal original
As Type) As Type
Dim target As
Type
target =
original
...
Return target
End Function
End
Class
Using this system we can create the invoice generator using
the following code:
Dim
generator As New
InvoiceGenerator
Of course the advantage of not having to explicitly code
the factory call also means that you can’t see from the object creation if
object is created directly or through the factory which can be seen as a
disadvantage L of this technique.
|