2. 工厂方法
Define an interface for creating an object, but let subclassses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类中。
——《设计模式:可复用面向对象软件的基础》
典型模式
我一直认为工厂方法是整个创建模式中最为典型的,也是最具启发效果的。它告诉我们使用一个变化频率比较高的类不必忙着new(),而要依赖一个抽象的类型(抽象类型或接口)。
适用于:
- 客户程序需要隔离其与需要创建的具体类型间的耦合关系。
- 客户程序在开发过程中还无法预知在生产环境中实际要提供给客户程序创建的具体类型。
- 将创建工作隔离在客户程序之外,客户程序仅需要执行自己的业务逻辑,把这部分职责交给外部对象完成。
public interface IProduct
{
string Name { get; }
}
public class ConcreteProductA : IProduct
{
public string Name { get { return "Product A"; } }
}
public class ConcreteProductB : IProduct
{
public string Name { get { return "Product B"; } }
}
public interface IFactory
{
IProduct Create();
}
public class FactoryA<T> : IFactory<T>
{
public IProduct<T> Create()
{
return new ConcreteProductA<T>();
}
}
角色:
- 抽象产品类型 (IProduct):工厂要加工的对象所需要的抽象特征实体。
- 具体产品类型(ContreteProduct):实现客户程序所需要的抽象特质的类型,它就是工厂需要延迟实例的备选对象。
- 抽象工厂类型(IFactory):定义一个工厂方法的默认实现,它返回抽象产品类型。
- 具体工厂类型(ContreteFactory):返回具体产品类型的具体工厂。
使用:
public class Client
{
public void Method()
{
IFactory factory = new FactoryA();
IProduct product = factory.Create();
}
}
public class Client2
{
private IFactory factory;
public Client2(IFactory factory)
{
this.factory = factory;
}
public void Method()
{
IProduct product = this.factory.Create();
}
}
扩展
基于泛型的工厂方法
随着泛型的使用,IProduct的含义也被扩展了,项目中经常需要提供IProduct之类的泛型抽象产品类型。这些类型在类库的最外层往往被赋予了具体的类型参数。但是在内层类库,往往会保持泛型的抽象类型。为了保持工厂类型的加工过程的通用性,也需要设计具有泛型的工厂方法。
场景:
应用中有一个自定义的链表结构IlinkList,客户程序有时用它来保存学生历年的成绩,类型为ILinkList;有时用它来保存一个班级内学生的姓名,类型为ILinkList。如果我们要实现ILinkList和ILinkList的具体工厂类型,有两种选项:
- 选择一:分别定义MarkFactory和NameFactory工厂,用于生成ILinkList 和 ILinkList。
- 选择二:定义一个ILinkList,由客户程序根据需要提供具体的类型参数。
哪种实现更好呢?“视情况而定”。如果直接服务于最外层的业务逻辑,那么选择第一种方式最好;如果继续用于内层抽象算法描述,那么选择第二种方式更能保持算法的通用性。
public interface IProduct<T>
{
string Name { get; }
}
public class ConcreteProductA<T> : IProduct<T>
{
public string Name { get { return "Product A"; } }
}
public class ConcreteProductB<T> : IProduct<T>
{
public string Name { get { return "Product B"; } }
}
public interface IFactory<T>
{
IProduct<T> Create();
}
public class FactoryA<T> : IFactory<T>
{
public IProduct<T> Create()
{
return new ConcreteProductA<T>();
}
}
基于泛型约束的工厂类型
工厂方法的目的是将用户程序需要的类型选择推迟到子类,为什么不能把非常通用的new()也延迟到自己子类呢?
public class FactoryBase<T> : IFactory where T : new()
{
public IProduct Create()
{
return new T() as IProduct;
}
}
public class FactoryA2 : FactoryBase<ConcreteProductA> { }
Unit Test:
[TestMethod]
public void Test_GenericFactoryMethod()
{
IFactory factory = new FactoryA2();
IProduct product = factory.Create();
Assert.IsNotNull(product);
Assert.IsInstanceOfType(product, typeof(ConcreteProductA));
}
这些些工厂都是FactoryBase的子类,大家都有一个通用的IProduct Create() 方法,这样的代码多整齐。
最大的好处是整个体系只有一个根对象FactoryBase, 整个体系统一。
另外一个好处是,统一了方法名称,如果有多个工厂方法,而且不做任何约束的话,可能会出现各种Create(), CreateInstance(), Constructor()等“大杂烩”名称。