设计模式基于C#的实现与扩展——创建型模式(三)

本文详细介绍了抽象工厂模式的概念和应用场景,强调了它在创建一系列相关或相互依赖对象时的作用。文章回顾了其他工厂模式,指出抽象工厂模式可以提供一个创建多个相关产品的接口,以减少客户程序的复杂性。文章还讨论了抽象工厂模式的典型实现,扩展包括带类型映射的抽象工厂和基于委托的工厂,以及对这两种扩展的优缺点进行了分析。
摘要由CSDN通过智能技术生成

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));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值