设计模式(3) 抽象工厂模式

  • 抽象工厂模式
  • 优化抽象工厂
  • 异步工厂

在学习抽象工厂模式前,先来回顾一下前面的简单工厂和工厂方法模式。简单工厂的职责非常简单:构造某个实体类型,然后把实例作为抽象类型返回;
工厂方法模式则进一步抽象出一个抽象的创建者和一个抽象的产品类型,而实际的执行过程是具体工厂创建具体的产品类型,具体工厂和具体产品类型都可以被抽象为之前定义的抽象创建者和抽象产品类型,这种模式即便面对的是一个很庞大的具有复杂家族关系的类型系统,客户程序在操作的过程中仍然可以基于抽象创建者获得满足某种抽象类型的产品实例。

但在很多场景下,需要创建的不是仅仅继承自单个抽象类型的产品,它们本身就是多个具有一定依赖关系,但非同源的类型。

抽象工厂模式

抽象工厂模式可以应对这种情况,它能够产生一系列具有相关依赖关系的类型。
Provide an interface for creating families of related or dependent objects.
— Design Patterns : Elements of Reusable Object-Oriented Software

抽象工厂可以返回一系列相关或相互依赖对象的接口。另外抽象工厂自身也需要一个接口,这个接口定义中包括返回那些相关对象接口的方法定义。

其UML类图如下:
抽象工厂 UML类图
其中IProductA IProductB就是相关或相互依赖对象的接口,实体工厂会生产实现了这些接口的实体产品。IAbstractFactory是抽象工厂的接口,定义了生产IProductA、IProductB的方法。实体工厂自行决定如何实现抽象工厂接口定义的生产方法。

实现代码:

public interface IProductA { };
public interface IProductB { };

public interface IAbstractFactory
{
    IProductA CreateProductA();
    IProductB CreateProductB();
}

public class ProductA1 : IProductA { }
public class ProductA2 : IProductA { }
public class ProductB1 : IProductB { }
public class ProductB2 : IProductB { }

public class ConcreteFactory1 : IAbstractFactory
{
    public IProductA CreateProductA()
    {
        return new ProductA1();
    }

    public IProductB CreateProductB()
    {
        return new ProductB1();
    }
}

public class ConcreteFactory2 : IAbstractFactory
{
    public IProductA CreateProductA()
    {
        return new ProductA2();
    }

    public IProductB CreateProductB()
    {
        return new ProductB2();
    }
}

调用:

[Test]
public void AbstractFactoryTest()
{
    IAbstractFactory factory = new ConcreteFactory1();
    IProductA productA = factory.CreateProductA();
    IProductB productB = factory.CreateProductB();

    Assert.AreEqual(typeof(ProductA1), productA.GetType());
    Assert.AreEqual(typeof(ProductB1), productB.GetType());
}

从调用端代码可以发现一个问题,同前面工厂方法模式一样,Client与某个具体工厂耦合在一起,所以这里也可以采用依赖注入的方式“解决”这个问题,把这一步的处理推给上一层的调用端。

优化抽象工厂模式

这套基于经典的抽象工厂模式的代码还有可以优化的地方:

  • 首先具体工厂的代码重复性太高,
  • 另外具体工厂与具体的产品绑定,如果需要改变产品组合的方式,就得要么修改具体工厂的代码,要么创建新的工厂,改动都很大。
  • 还有个更严重的问题,如果要添加新的抽象产品,那么从抽象工厂接口到每个具体实现都需要修改,几乎是牵一发动全身。

针对第一点的优化方案,可以提取出一个具体工厂共用的基类AbstractFactoryBase,让它实现抽象接口的创建方法,如果某个工厂的Create方法比较特殊,可以重写基类的Create方法。
但是基类工厂并不知道具体工厂要创建的是怎样的产品组合,这可以在实例化具体工厂的时候传递一个抽象产品与具体产品的映射(可以是字典的形式),让基类根据映射关系来运作,或者可以基于现成的IOC容器来配置这样的映射。这样第二个、第三个问题也就都迎刃而解了。
实现代码如下:

public interface IAbstractFactoryWithMapper
{
    T Create<T>() where T : class;
}

public abstract class AbstractFactoryBase : IAbstractFactoryWithMapper
{
    protected IDictionary<Type, Type> mapper;
    public AbstractFactoryBase(IDictionary<Type, Type> mapper)
    {
        this.mapper = mapper;
    }

    public virtual T Create<T>() where T : class
    {
        if (mapper == null || mapper.Count == 0 || !mapper.ContainsKey(typeof(T)))
        {
            throw new ArgumentNullException();
        }
        Type targetType = mapper[typeof(T)];
        return (T)Activator.CreateInstance(targetType);
    }
}

public class ConcreteFactory : AbstractFactoryBase
{
    public ConcreteFactory(IDictionary<Type, Type> mapper) : base(mapper) { }
}

调用:

[Test]
public void AbstractFactoryWithMapperTest()
{
    IDictionary<Type, Type> dictionary = new Dictionary<Type, Type>();
    dictionary.Add(typeof(IProductA), typeof(ProductA1));
    dictionary.Add(typeof(IProductB), typeof(ProductB1));

    IAbstractFactoryWithMapper factory = new ConcreteFactory(dictionary);
    IProductA productA = factory.Create<IProductA>();
    IProductB productB = factory.Create<IProductB>();

    Assert.AreEqual(typeof(ProductA1), productA.GetType());
    Assert.AreEqual(typeof(ProductB1), productB.GetType());
}

异步工厂

有些时候工厂创建产品实例的过程比较复杂,或者涉及网络、数据库等外部资源的访问,整体耗时较长;这种情况下,如果工厂支持异步调用,客户程序就可以只向工厂发一个请求,然后接着干别的事,等收到工厂创建完成的通知后再回来接着处理。
异步工厂实现:

public interface IProduct { };
public interface IFactory
{
    IProduct Create();
}

public interface IFactoryWithNotifier : IFactory
{
    void Create(Action<IProduct> callBack);
}

//实体结构部分
public class ConcreteProduct : IProduct { }
public class ConcreteFactory : IFactoryWithNotifier
{
    public IProduct Create() //同步构造
    {
        return new ConcreteProduct();
    }

    public void Create(Action<IProduct> callBack)  //异步构造
    {
        IProduct product = Create();
        callBack(product);
    }
}

//为方便单元测试构造的订阅者
public class Subscriber
{
    private IProduct product;
    public void SetProduct(IProduct product)
    {
        this.product = product;
    }

    public IProduct GetProduct()
    {
        return product;
    }
}

调用:

[Test]
public void AsyncFactoryTest()
{
    IFactoryWithNotifier factoryWithNotifier = new ConcreteFactory();
    Subscriber subscribe = new Subscriber();
    Action<IProduct> callback = new Action<IProduct>(subscribe.SetProduct);

    Assert.IsNull(subscribe.GetProduct());
    factoryWithNotifier.Create(callback);
    Assert.IsNotNull(subscribe.GetProduct());
}

参考书籍:
王翔著 《设计模式——基于C#的工程化实现及扩展》

posted @ 2020-07-05 17:51  zhixin9001  阅读(217)  评论(0编辑  收藏  举报