3. 抽象工厂
Provide an interface for creating familyes of related or dependent objects.
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类型。
——《设计模式:可复用面向对象软件的基础》
在介绍抽象工厂模式之前,我们先回顾下其他工厂模式的核心目标:直接由客户程序创建对象的时候,我们通过工厂把这个创建过程封装起来,让客户程序不需自己new 目标类型。
回到我们的项目实际,有时候我们要创建的不是继承自“一个”抽象类型的产品,它们本身就是多个具有一定依赖关系,但非同源的类型,因此,需要提供另一种形式的工厂,它可以产生“一系列”具有相关依赖关系的类型。
对比现实中的工厂,比如,一个中餐馆不仅提供炸酱条,还提供酸梅汤;另外一家酒店的餐厅也提供中餐,但是口味不同,提供阳春面和鲜榨果汁。如果采用工厂方法模式完成这个工作,我们可以定义INoodle和IDrink,然后再实现“阳春面”、“炸酱面”之类的不同类型,并设计不同的工厂,最后客户程序调用INoodleFactory和IDrinkFactory获得具体的食物。这个思路是可以实现的,但是太繁琐了。
我们可以换个思路,抽象出一个IRestaurant,它可以生产INoodle和IDrink,客户程序面对的是一个个“店”,通过这个“店”获得自己需要的各种食品。
典型模式
抽象工厂的意图:
- 可以返回一系列相关或相互依赖对象的接口
- 需要指定一个接口,也就是抽象工厂,它的接口定义中包括返回那些相关对象接口的方法定义。
— 类图在此—
public interface IProduct
{
string Name { get; }
}
public interface IProduct2
{
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 class ConcreteProduct2A : IProduct2
{
public string Name { get { return "Product 2A"; } }
}
public class ConcreteProduct2B : IProduct2
{
public string Name { get { return "Product 2B"; } }
}
public interface IAbstractFactory
{
IProduct CreateProduct();
IProduct2 CreateProduct2();
}
public class ConcreteFactoryA : IAbstractFactory
{
public IProduct CreateProduct()
{
return new ConcreteProductA();
}
public IProduct2 CreateProduct2()
{
return new ConcreteProduct2A();
}
}
public class ConcreteFactoryB : IAbstractFactory
{
public IProduct CreateProduct()
{
return new ConcreteProductB();
}
public IProduct2 CreateProduct2()
{
return new ConcreteProduct2B();
}
}
适用于:
- 客户程序希望将业务逻辑和产品创建相分离,把一系列产品的创建,甚至一些系列中某些产品的组合全部放在抽象工厂内部解决。
- 当你要强调一系列相关的产品对象的设计,便于客户程序集中调用。
硬伤:
抽象工厂模式的硬伤是,它不适合实体类型快速变化的情况。例如,如果在IProductA和IProductB以外,还需要增加一个“相关或相互依赖”的IProductC,该怎么办呢?那除了修改IAbstractFactory外,还需要修改所有的ConcreteFactory。
扩展
带类型映射的抽象工厂
我们发现CreateProductA() 和 CreateProductB()的方法实在太像了,反复做这种Ctrl + C & Ctrl + V的工作实在没意思,那我们就做些修改。
public interface IAbstractFactoryWithMap
{
T Create<T>() where T : class;
}
public class AbstractFactoryBase : IAbstractFactoryWithMap
{
protected IDictionary<Type, Type> _Mapper;
public AbstractFactoryBase(IDictionary<Type, Type> mapper)
{
_Mapper = mapper;
}
public T Create<T>() where T : class
{
if (_Mapper == null || _Mapper.Count == 0||
!_Mapper.ContainsKey(typeof(T)))
throw new ArgumentException("T");
Type targetType = _Mapper[typeof(T)];
return Activator.CreateInstance(targetType) as T;
}
}
Unit Test:
private IAbstractFactoryWithMap AssemblyFactory()
{
IDictionary<Type, Type> mapper = new Dictionary<Type, Type>();
mapper.Add(typeof(IProduct), typeof(ConcreteProductA));
mapper.Add(typeof(IProduct2), typeof(ConcreteProduct2B));
return new AbstractFactoryBase(mapper);
}
[TestMethod]
public void Test_MapFactory()
{
IAbstractFactoryWithMap factory = this.AssemblyFactory();
IProduct product = factory.Create<IProduct>();
Assert.IsNotNull(product);
Assert.IsInstanceOfType(product, typeof(ConcreteProductA));
IProduct2 product2 = factory.Create<IProduct2>();
Assert.IsNotNull(product2);
Assert.IsInstanceOfType(product2, typeof(ConcreteProduct2B));
}
通过这个改进,你也应该感觉到了,其实经典工厂模式可以通过很多方式改进,一个最直接的办法就是增加一个“Concrete Product / Abstract Product”的字典对象(TypeMapper),这个字典对象可以是通过配置文件、数据库或者其他什么方式建立,IAbstractFactory与ConcreteFactory对此并不关注。
— 类图在此 —
优势:
- 大大降低类库的编码量,应用中“一系列相关或相互依赖”的类型都可以套用样板快速完成。
局限:
- 通用性太强的问题容易导致对个性问题的处理不够理想,如果在构建ConcreteProduct的过程有很多差异,此时就只好退回到经典抽象工厂模式中“一事一议”的方式。
基于委托的工厂
相对于使用依赖注入来实现对ConcreteFactory的指定,还可以转化为将Factory中的Create方法委托出去,从而解耦对ConcreteFactory的依赖。
public interface IAbstractFactoryAll<T1, T2>
{
T1 CreateProduct();
T2 CreateProduct2();
}
public delegate IProduct ProductHandler();
public delegate IProduct2 Product2Handler();
internal class Calculator
{
public IProduct GenerateProduct()
{
ConcreteFactoryA factory = new ConcreteFactoryA();
return factory.CreateProduct();
}
public IProduct2 GenerateProduct2()
{
ConcreteFactoryB factory = new ConcreteFactoryB();
return factory.CreateProduct2();
}
}
public class DelegateFactory : IAbstractFactoryAll<ProductHandler, Product2Handler>
{
public ProductHandler CreateProduct()
{
return new Calculator().GenerateProduct;
}
public Product2Handler CreateProduct2()
{
return new Calculator().GenerateProduct2;
}
}
Unit Test:
[TestMethod]
public void Test_DelegateFactory()
{
IAbstractFactoryAll<ProductHandler, Product2Handler> factory = new DelegateFactory();
ProductHandler handler = factory.CreateProduct();
IProduct product = handler.Invoke();
Assert.IsNotNull(product);
Assert.IsInstanceOfType(product, typeof(ConcreteProductA));
Product2Handler handler2 = factory.CreateProduct2();
IProduct2 product2 = handler2.Invoke();
Assert.IsNotNull(product2);
Assert.IsInstanceOfType(product2, typeof(ConcreteProduct2B));
}
思考
仿照泛型的工厂方法写了一个基于泛型的抽象工厂。
public interface IAbstractFactoryGeneric<T1, T2>
where T1 : IProduct, new()
where T2 : IProduct2, new()
{
IProduct CreateProduct();
IProduct2 CreateProduct2();
}
public class ConcreteFactory<T1, T2> : IAbstractFactoryGeneric<T1, T2>
where T1 : IProduct, new()
where T2 : IProduct2, new()
{
public IProduct CreateProduct()
{
return new T1() as IProduct;
}
public IProduct2 CreateProduct2()
{
return new T2() as IProduct2;
}
}
public class GenericConcreteFactory : ConcreteFactory<ConcreteProductA, ConcreteProduct2B> { }
Unit Test:
[TestMethod]
public void Test_GenericAbstractFactory()
{
GenericConcreteFactory factory = new GenericConcreteFactory();
IProduct product = factory.CreateProduct();
Assert.IsNotNull(product);
Assert.IsInstanceOfType(product, typeof(ConcreteProductA));
IProduct2 product2 = factory.CreateProduct2();
Assert.IsNotNull(product2);
Assert.IsInstanceOfType(product2, typeof(ConcreteProduct2B));
}