创建型模式可以隔离客户程序对需要实例化类型的依赖关系,这类模式一般通过将实例化具体对象的职责委托给第三方对象的方式,使得客户程序或者外部系统在获得所需的具体类型实例的同时,而不必对其发生直接的引用。
创建型模式包括:
按照大多数设计模式书籍采用的顺序,首先从工厂方法模式开始。
简单工厂模式并没有被归入23种设计模式之列,但可以作为学习工厂方法模式前的预备。简单工厂模式在管理对象创建方面,提供的是最简单的方案,它仅仅简单的对不同类对象的创建进行了一层薄薄的封装,客户程序在使用时,通过向简单工厂传递一个类型来指定要创建的对象,其UML类图如下:
Client需要的是具体的产品ConcreteProductA或者ConcreteProductB,如果直接new()就会依赖对象实例,引入简单工厂后,Client变成了依赖IProduct和SampleFactory。
代码示例:
//产品接口
public interface IProduct { };
//具体产品
public class ConcreteProductA : IProduct { }
public class ConcreteProductB : IProduct { }
public enum Category { A, B }
//简单工厂
public class SampleFactory
{
public IProduct Create(Category category)
{
switch (category)
{
case Category.A:
return new ConcreteProductA();
case Category.B:
return new ConcreteProductB();
default:
throw new ArgumentOutOfRangeException();
}
}
}
调用:
[Test]
public void SampleFactoryTest()
{
SampleFactory sampleFactory = new SampleFactory();
IProduct product = sampleFactory.Create(Category.A);
Assert.AreEqual(product.GetType(), typeof(ConcreteProductA));
}
简单工厂模式中的工厂负责生产所有的产品类型,但如果工厂负责生产的产品只有一种,就可以进一步抽象了,这便出现了工厂模式。
GOF对工厂方法模式的描述是:
Define an interface for creating an object, but let subclasses decide which class toinstantiate. Factory Method lets a class defer instantiation to subclasses..
— Design Patterns : Elements of Reusable Object-Oriented Software
工厂方法模式定义了一个抽象的工厂,它的子类实体工厂都有统一通用的工厂方法,用来生产具体的产品,这样就把类的实例化延迟到其子类。
工厂方法主要有四个角色:
UML类图
代码示例:
//抽象产品类型
public interface IProduct
{
string Name { get; } //抽象产品所必须具有的特征
}
//具体产品类型
public class ProductA : IProduct
{
public string Name { get { return "A"; } }
}
public class ProductB : IProduct
{
public string Name { get { return "B"; } }
}
//抽象工厂类型
public interface IFactory
{
IProduct Create(); //抽象的工厂描述
}
//具体工厂类型
public class FactoryA : IFactory
{
public IProduct Create()
{
return new ProductA();
}
}
public class FactoryB : IFactory
{
public IProduct Create()
{
return new ProductB();
}
}
调用:
public class Client1
{
public void SomeMethod()
{
IFactory factory = new FactoryA();
IProduct product = factory.Create();
}
}
客户程序需要使用IProduct的时候,只需要获取到IFactory,通过调用其统一制定的Create()方法就可以获取到具体的产品,具体需要哪个产品由Factory决定。这样就隔离了客户程序对具体产品的依赖,但这里还有个问题是在获取IFactory的时候需要初始化具体的工厂,这样就对具体的工厂产生了依赖。
为了解决上述问题,可以把IFactory作为客户程序的参数,然后采用依赖注入的方式将具体的工厂注入:
public class Client2
{
private IFactory factory;
public Client2(IFactory factory)
{
this.factory = factory;
}
public string SomeMethod()
{
IProduct product = factory.Create();
return product.Name;
}
}
这样客户程序就变成了真正地只依赖IFactory和IProduct了,但问题是不论采用哪种依赖注入框架,都需要注册接口与实例的映射关系,那么这种方式就只是相当于把对具体产品的依赖“甩”到了“客户程序的客户程序”,怎么办呢,退无可退的时候,考虑把接口与实例的映射关系放到配置文件吧。
上面的工厂方法模式中,每个具体的工厂负责生产一种产品,且都实现了抽象工厂的Create()方法,于是借助泛型可以实现进一步的抽象:
public interface IFactory<T>
{
T Create(); //抽象的工厂描述
}
public abstract class FactoryBase<T> : IFactory<T> where T : new()
{
public virtual T Create()
{
return new T();
}
}
public class ProductAFactory : FactoryBase<ProductA> {
public override ProductA Create(){
return new ProductA();
}
}
public class ProductBFactory : FactoryBase<ProductB> { }
在抽象工厂和具体的工厂之间增加了支持泛型的FactoryBase,在这里实现了通用的Create()方法。如果某个具体工厂需要特殊的Create流程,只需重写虚方法即可。这样在工厂种类比较多而流程有大多相同的情况下,可以减少很多代码。
前面代码中的工厂生产的产品都是对象,如果需要生产的是方法呢,C#的委托机制可以方便地满足这种需求。
委托的本质是方法的指针,是对一类方法的抽象。所以委托本身就可以作为抽象产品的定义,而与委托签名一致的方法就是具体产品了。
代码示例:
public delegate int CalculateHandler(params int[] items);
public class Calculator
{
public int Add(params int[] items)
{
int result = 0;
foreach (var item in items)
{
result += item;
}
return result;
}
public int Multi(params int[] items)
{
int result = 1;
foreach (var item in items)
{
result *= item;
}
return result;
}
}
public class AddHandlerFactory : IFactory<CalculateHandler>
{
public CalculateHandler Create()
{
return new Calculator().Add;
}
}
public class MultiHandlerFactory : IFactory<CalculateHandler>
{
public CalculateHandler Create()
{
return new Calculator().Multi;
}
}
调用:
[Test]
public void CalculateHandlerTest()
{
CalculateHandler addHandler = new AddHandlerFactory().Create();
Assert.AreEqual(1 + 2 + 3, addHandler(1, 2, 3));
CalculateHandler multiHandler = new MultiHandlerFactory().Create();
Assert.AreEqual(1 * 2 * 3, multiHandler(1, 2, 3));
}
参考书籍:
王翔著 《设计模式——基于C#的工程化实现及扩展》
联系客服