工厂模式:枚举还是类型?

7

在实现工厂或简单工厂时,使用Type而不是Enum来指定要实例化的类,有什么问题吗?

例如:

public class SimpleFactory
{
 public static ITest Create(Type type)
 {
  if (type == typeof(ConcreteTest1))
   return new ConcreteTest1();
  if (type == typeof(ConcreteTest2))
   return new ConcreteTest2();

  throw new Exception("Invalid type");
 }
}
6个回答

16

使用枚举更加限制,这意味着用户不太可能会尝试使用不支持的类型来调用您的工厂。

我发现,在定义 API 时尽可能做到一切都是有益的,以防止导致异常抛出的使用模式。在这种情况下允许"Type"会打开成千上万种调用函数的方式,最终可能导致:

throw new Exception("Invalid type");

使用枚举类型可以消除这个问题。只有当用户明显做错了事情时,枚举类型才会抛出异常。


2
我完全同意!编译时约束更容易执行和使用。 - jeremyalan

6
工厂仅在对对象执行配置或初始化以将其放置在有效状态时才有用。如果它仅新建对象并返回对象,那么我不会费心使用工厂。
我为每个类层次结构创建一个工厂。例如:
public abstract class Vehicle {}
public class Car : Vehicle {}
public class Truck : Vehicle {}

public class VehicleFactory
{
    public Vehicle CreateVehicle<T>() where T : Vehicle
    {
        // Get type of T and delegate creation to private methods
    }
}

2
我更倾向于使用通用约束,因为仅为指定所需对象的类型而使用枚举似乎对我来说有些冗余,并且使用您描述的类型会违反开闭原则。与您所做的不同之处在于,我会限制您的类型,以便只能传递允许的类型。
我将使用c#中的泛型举例说明。
public class SimpleFactory
{
 public static ITest Create<T>()
    where T: ITest, new()
 {
    return new T();
 }
}

然后您将使用ConcreteTest1和ConcreteTest2实现IConcreteTest,并且您可以像这样使用您的工厂:

ConcreteTest1 test1 = SimpleFactory.Create<ConcreteTest1>();

将类型传递给通用方法如何解决问题?这是否仍然需要调用者对T有一些高级知识? - jeremyalan
是的,但在他的示例中,他已经知道了类型,他将其传递到方法中。唯一的区别是我将其用作泛型,这样您就可以执行类似于SimpleFactory.Create<ConcreteTest1>()的操作。 - Joseph
他没有给出typeof的实现,因此我们不能确定他是否具有类型的高级知识并能够传递给调用。 - Rohit
3
@Rohit实现typeof?typeof是一个关键字(http://msdn.microsoft.com/en-us/library/58918ffs(VS.71).aspx),他具有先进的知识,否则他不会将其作为参数传递。 - Joseph
如果你询问如何传递 ENUM,那么肯定你已经具备了高级知识。 - ScottCate

2

如果你想要一个无懈可击的工厂,那么你必须为每种具体类型创建一个具体工厂。这个类不遵循开闭原则:每次你得到一个新的具体类型,你都需要重新编辑这个类。

在我看来,更好的方法是使用继承,为每种具体类型创建一个具体工厂类。


你如何决定使用哪个具体工厂?你仍然需要上述条件吗? - theringostarrs
如果您使用一个包含上述条件语句的FactoryLoader类型类,以便从具体工厂本身中移除逻辑,那么当向FactoryLoader逻辑添加新的条件/工厂类型时,如何避免违反开闭原则?我们难道不是只是把问题转移到了别处吗? - theringostarrs
我猜IoC容器会处理这个问题,但是你如何以纯粹的方式完成呢? - theringostarrs

1
如果想要通过类型来创建对象,你可以直接使用Activator.CreateInstance(Type t)方法。为了将其限制到特定接口,你可以在一个模板方法中进行包装,例如Create<T> where T:ITest

1

我认为我最担心的问题是工厂的目的是允许客户端代码创建一个派生对象实例,而不知道正在创建的类型的细节(更具体地说,如何创建实例的细节,但如果正确执行,调用者不应该需要知道除基类提供的内容之外的任何细节)。

使用从派生类型中提取的类型信息仍然需要调用者对他想要实例化的类型有一些亲密的了解,这使得更新和维护变得困难。通过替换枚举类型(或字符串、整数等),您可以更新工厂,而无需更新调用代码以了解新的派生类型。

我想有人可能会认为类型名称可以作为字符串从配置文件、数据库等中读取,并使用Reflections(在.NET中)或RTTI(在C++中)确定类型信息,但我认为这更适合简单地使用类型字符串作为您的标识符,因为它将有效地发挥相同的作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接