一、引言
在上一专题中介绍了工厂方法模式,工厂方法模式是为了克服简单工厂模式的缺点而设计出来的,简单工厂模式的工厂类随着产品类的增加需要增加额外的代码,而工厂方法模式每个具体工厂类只完成单个实例的创建,所以它具有很好的可扩展性。但是在现实生活中,一个工厂只创建单个产品这样的例子很少,因为现在的工厂都多元化了,一个工厂创建一系列的产品,如果我们要设计这样的系统时,工厂方法模式显然在这里不适用,然后抽象工厂模式却可以很好地解决一系列产品创建的问题,这是本专题所要介绍的内容。
二、抽象工厂详细介绍
随着客户的要求越来越高,宝马车需要不同配置的空调和发动机等配件。于是这个工厂开始生产空调和发动机,用来组装汽车。这时候工厂有两个系列的产品:空调和发动机。宝马320系列配置A型号空调和A型号发动机,宝马230系列配置B型号空调和B型号发动机。
概念:
抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。比如宝马320系列使用空调型号A和发动机型号A,而宝马230系列使用空调型号B和发动机型号B,那么使用抽象工厂模式,在为320系列生产相关配件时,就无需制定配件的型号,它会自动根据车型生产对应的配件型号A。
针对百度百科上对于抽象工厂模式的简介,结合本例如下:
当每个抽象产品都有多于一个的具体子类的时候(空调有型号A和B两种,发动机也有型号A和B两种),工厂角色怎么知道实例化哪一个子类呢?比如每个抽象产品角色都有两个具体产品(产品空调有两个具体产品空调A和空调B)。抽象工厂模式提供两个具体工厂角色(宝马320系列工厂和宝马230系列工厂),分别对应于这两个具体产品角色,每一个具体工厂角色只负责某一个产品角色的实例化。每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。
抽象工厂模式代码
/// <summary>
/// 发动机以及型号
/// </summary>
public interface IEngine
{
}
public class EngineA : IEngine
{
public EngineA()
{
Console.WriteLine("制造-->EngineA");
}
}
public class EngineB : IEngine
{
public EngineB()
{
Console.WriteLine("制造-->EngineB");
}
}
/// <summary>
/// 空调以及型号
/// </summary>
public interface IAircondition
{
}
public class AirconditionA : IAircondition
{
public AirconditionA()
{
Console.WriteLine("制造-->AirconditionA");
}
}
public class AirconditionB : IAircondition
{
public AirconditionB()
{
Console.WriteLine("制造-->AirconditionB");
}
}
/// <summary>
/// 创建工厂的接口
/// </summary>
public interface IAbstractFactory
{
/// <summary>
/// 制造发动机
/// </summary>
/// <returns></returns>
IEngine CreateEngine();
/// <summary>
/// 制造空调
/// </summary>
/// <returns></returns>
IAircondition CreateAircondition();
}
/// <summary>
/// 为宝马320系列生产配件
/// </summary>
public class FactoryBMW320 : IAbstractFactory
{
public IEngine CreateEngine()
{
return new EngineA();
}
public IAircondition CreateAircondition()
{
return new AirconditionA();
}
}
/// <summary>
/// 宝马523系列
/// </summary>
public class FactoryBMW523 : IAbstractFactory
{
public IEngine CreateEngine()
{
return new EngineB();
}
public IAircondition CreateAircondition()
{
return new AirconditionB();
}
}
public class Customer
{
public static void Main()
{
//生产宝马320系列配件
FactoryBMW320 factoryBMW320 = new FactoryBMW320();
factoryBMW320.CreateEngine();
factoryBMW320.CreateAircondition();
//生产宝马523系列配件
FactoryBMW523 factoryBMW523 = new FactoryBMW523();
factoryBMW523.CreateEngine();
factoryBMW523.CreateAircondition();
}
}
上面代码中都有详细的注释,这里就不再解释上面的代码了,下面就具体看看抽象工厂模式的定义吧:
抽象工厂模式:提供一个创建产品的接口来负责创建相关或依赖的对象,而不具体明确指定具体类。
抽象工厂允许客户使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么。这样客户就可以从具体产品中被解耦。
抽象工厂应对需求变更
看完上面抽象工厂的实现之后,如果要新增“911”车系,911车系要使用发动机C和空调C怎么办呢?下面就具体看看应用了抽象工厂模式的系统是如何应对这种需求的。
public class EngineC : IEngine
{
public EngineC()
{
Console.WriteLine("制造-->EngineC");
}
}
public class AirconditionC : IAircondition
{
public AirconditionC()
{
Console.WriteLine("制造-->AirconditionC");
}
}
/// <summary>
/// 宝马911系列
/// </summary>
public class FactoryBMW911 : IAbstractFactory
{
public IEngine CreateEngine()
{
return new EngineC();
}
public IAircondition CreateAircondition()
{
return new AirconditionC();
}
}
public class Customer
{
public static void Test()
{
//生产宝马320系列配件
FactoryBMW320 factoryBMW320 = new FactoryBMW320();
factoryBMW320.CreateEngine();
factoryBMW320.CreateAircondition();
//生产宝马523系列配件
FactoryBMW523 factoryBMW523 = new FactoryBMW523();
factoryBMW523.CreateEngine();
factoryBMW523.CreateAircondition();
//生产宝马911系列配件
FactoryBMW911 factoryBMW911 = new FactoryBMW911();
factoryBMW911.CreateEngine();
factoryBMW911.CreateAircondition();
}
}
只需要添加三个类:一个是911车系具体工厂类,负责创建911车系的空调和发动机,另外两个类是C型号的发动机类和空调类。从上面代码看出,抽象工厂对于系列产品的变化支持 “开放——封闭”原则(指的是要求系统对扩展开放,对修改封闭),扩展起来非常简便,但是,抽象工厂对于添加新产品这种情况就不支持”开放——封闭 “原则,这也是抽象工厂的缺点所在,这点会在以后部分详细介绍。
三、抽象工厂的分析
抽象工厂模式将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展,这真是抽象工厂模式的优点所在,然后抽象模式同时也存在不足的地方。下面就具体看下抽象工厂的缺点(缺点其实在前面的介绍中以已经涉及了):
抽象工厂模式很难支持新种类产品的变化。这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这样也就违背了“开发——封闭”原则。
知道了抽象工厂的优缺点之后,也就能很好地把握什么情况下考虑使用抽象工厂模式了,下面就具体看看使用抽象工厂模式的系统应该符合那几个前提:
1) 一个系统不要求依赖产品类实例如何被创建、组合和表达,这点也是所有工厂模式应用的前提。
2) 这个系统有多个系列产品,而系统中只消费其中某一系列产品
3) 系统要求提供一个产品类的库,所有产品以同样的接口出现,客户端不需要依赖具体实现。
显然,一个系统只能够在某一个操作系统的视窗环境下运行,而不能同时在不同的操作系统上运行。所以,系统实际上只能消费属于同一个产品族的产品。
在现代的应用中,抽象工厂模式的使用范围已经大大扩大了,不再要求系统只能消费某一个产品族了。
总结:
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。
所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。