委托工厂






2.92/5 (8投票s)
使用委托的动态工厂
引言
本文的目标是展示另一种使用委托的工厂实现。我曾在 CodeProject 上搜索过类似的实现,虽然有一些很接近,但它们并不完全相同。所以我决定发布我当前项目中使用的这个工厂。
本文的目标不是解释工厂的整个概念,也不是解释为什么你会使用这种特定的设计模式。
目录
目标
编写这个工厂的目标如下
- 动态工厂 - 将来有可能在不打开工厂代码的情况下,将同一工厂用于新型对象。
- 创建责任 - 工厂不应该知道如何创建对象。
- 易于使用。
使用委托
使用委托将帮助我们实现目标 1 和 2,它可能会让我们与目标 3 发生冲突,但我们会看到。
委托的声明如下所示
/// <SUMMARY>
/// A handler function for the factory to create objects;
/// </SUMMARY>
public delegate AObject ObjectCreator(params object [] list);
这个委托将帮助我们隐藏对象从工厂创建的方式,并帮助我们在工厂上注册这种类型的委托。
实现对象层次结构
工厂将在我们的树中创建我们的第一个可用对象,这样我们就不必直接向下转型(只有在真正需要的情况下)。
基类是 abstract
并且定义了一个变量成员和一个 abstract
函数
/// <SUMMARY>
/// Summary description for AObject.
/// </SUMMARY>
public abstract class AObject
{
#region Override
/// <SUMMARY>
/// Force all descendents to implement.
/// </SUMMARY>
public abstract void Print();
#endregion
#region protected
/// <SUMMARY>Some variable to play with.</SUMMARY>
protected Int32 m_nType;
#endregion
}
派生类如下所示
/// <SUMMARY>
/// Summary description for Class1.
/// </SUMMARY>
public class Class1 : AObject
{
#region Constants
/// <SUMMARY>
/// The class type identifier.
/// </SUMMARY>
public const Int32 ClassType = 1;
#endregion
#region C'tor
/// <SUMMARY>
/// Default C'tor.
/// </SUMMARY>
internal Class1()
{
this.m_nType = ClassType;
}
#endregion
#region Overrides
/// <SUMMARY>
/// Implementation of Print.
/// </SUMMARY>
public override void Print()
{
String msg = String.Format("Class: {0, 20} Value: {1, 10}",
ToString(), m_nType*67);
Console.WriteLine(msg);
}
#endregion
#region Static
/// <SUMMARY>
/// A handler function for the factory to create objects;
/// </SUMMARY>
/// The parameter list.
/// <RETURNS>A Class1 object.</RETURNS>
public static AObject ObjectCreator(params object[] list)
{
return new Class1();
}
#endregion
}
正如你所看到的,我们为派生类实现了一个 Print
函数,它做了一些事情(希望与其他的派生类不同),并且我们添加了一个 static
函数,用于创建 Class1
的新对象。
static
函数 ObjectCreator
负责在类中创建对象。
工厂
factory
类借助委托将类的 static
函数映射到我们想要创建的类型。
映射是通过一个哈希表完成的,其中对象的类型是键,封装 static
函数的委托是 值
。
工厂
/// <SUMMARY>
/// Summary description for ObjectFactory.
/// </SUMMARY>
public class ObjectFactory
{
#region Static
/// <SUMMARY>
/// Register handler functions to create new types of objects.
/// </SUMMARY>
/// The type of the object.
/// The handler function.
/// <RETURNS>true if successful.</RETURNS>
public static bool RegisterHandler(Int32 type, ObjectCreator creator)
{
bool res = false;
try
{
if (m_handlers[type] != null)
return false;
// insert the handler to the table according to the type.
m_handlers[type] = creator;
res = true;
}
catch(Exception ex)
{
Console.WriteLine("Can't register handler - "+ex.Message);
}
return res;
}
/// <SUMMARY>
/// Unregister handler functions according to type.
/// </SUMMARY>
/// The type of the object.
/// <RETURNS>true if successful.</RETURNS>
public static bool UnregisterHandler(Int32 type)
{
bool res = true;
try
{
if (m_handlers[type] == null)
return res;
// remove the handler to the table according to the type.
m_handlers[type] = null;
GC.Collect();
}
catch(Exception ex)
{
Console.WriteLine("Can't unregister handler - "+ex.Message);
res = false;
}
return res;
}
/// <SUMMARY>
/// This is the static method that creates all types of objects.
/// </SUMMARY>
/// <REMARKS>Factory method.</REMARKS>
/// The key of objects to create.
/// The parameter list for the object.
/// <RETURNS>An object.</RETURNS>
public static AObject CreateObject(Int32 type, params object [] list)
{
AObject aobject = null;
try
{
// get the handler that creates the objects
ObjectCreator creator = (ObjectCreator)m_handlers[type];
// create the object with the handler.
if (creator != null)
aobject = creator(list);
}
catch(Exception ex)
{
Console.WriteLine("Can't get object from handler - "+ex.Message);
}
return aobject;
}
#endregion
#region Protected
/// <SUMMARY> A table holding the handlers for creating objects. </SUMMARY>
protected static Hashtable m_handlers = new Hashtable();
#endregion
}
请注意,factory
有 register
和 unregister
函数,稍后,我们将看到如何使用它们。
RegisterHandler
-register
函数接受一个委托,并将其插入哈希表,其中类标识符作为键。UnregisterHandler
-unregister
函数接受type
并删除作为值
的任何委托。CreateObject
- 根据其type
(键)创建对象的函数。这个函数从哈希表中提取一个委托,从type
的位置 并调用委托(调用我们之前注册的对象的static
函数)。
使用工厂
在收到许多受人尊敬的同事的评论后,我稍微修改了以下代码,这样更明显的是,工厂并不知道它得到什么类型,而是检查它是否知道如何使用提供的键(类型)创建对象。
static void Main(string[] args)
{
try
{
// registering the types that the factory will create
ObjectFactory.RegisterHandler(Class1.ClassType,
new ObjectCreator(Class1.ObjectCreator));
ObjectFactory.RegisterHandler(Class2.ClassType,
new ObjectCreator(Class2.ObjectCreator));
ObjectFactory.RegisterHandler(Class3.ClassType,
new ObjectCreator(Class3.ObjectCreator));
AObject aobject = null;
// creating the objects
for (int i = 0; i<100; i++)
{
aobject = ObjectFactory.CreateObject(i%3+1, null);
aobject.Print();
}
// unregistering a type
if (!ObjectFactory.UnregisterHandler(Class1.ClassType))
Console.WriteLine("Really ?!");
// trying to create an unregistered type
aobject = ObjectFactory.CreateObject(Class1.ClassType, null);
if (aobject != null)
aobject.Print();
else
Console.WriteLine("aobject is null");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
注意注册过程
我们传递一个要注册的 type
,并且我们创建一个新的委托,将注册类型的 static
函数传递给它。我们必须确保在我们可以使用工厂创建对象之前注册 type
,所以注册过程应该尽可能早地在程序中进行。
还要注意的一件事是,Class3
(如果你已经下载了代码,你可能已经注意到)属于与 Main
相同的 namespace
(和程序集),而不是属于其他类所属的 namespace
(和程序集)。这意味着我们已经实现了目标 1 和目标 2。
我留给你来决定是否实现了目标 3。