抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。根据LSP原则,任何接受父类型的地方,都应当能够接受子类型。因此,实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例。 总的来说就是提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
当每个抽象产品都有多于一个的具体子类的时候,工厂角色怎么知道实例化哪一个子类呢?比如每个抽象产[1]品角色都有两个具体产品。抽象工厂模式提供两个具体工厂角色,分别对应于这两个具体产品角色,每一个具体工厂角色只负责某一个产品角色的实例化。每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。
每一个模式都是针对一定问题的解决方案,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式针对的是多个产品等级结构。
(1)抽象工厂( Creator )角色
抽象工厂接口,包含所有产品创建的抽象方法,是抽象工厂方法模式的核心。
(2)具体工厂(Concrete Creator )角色
通常我们在运行时创建一个具体工厂的实例,这个具体工厂在创建具有特定实现的产品对象。
(2)抽象产品( Product )角色
抽象工厂模式所创建所有对象的父类,他负责描述所有实例所共有的公共接口。
(3) 具体产品( Concrete Product )角色
抽象工厂模式的创建目标,所有创建的对象都是充当这个角色某个具体类的实例。
UML图
我们以一个常用的数据库切换为例看,来体验抽象工厂
web层 接口层 Sqlserver数据访问 Mongodb数据访问
目录结构基本如下
抽象产品A,抽象产品B
/// <summary> /// 产品A Interface /// </summary> public interface IProductA { /// <summary> /// 介绍产品A(方便测试) /// </summary> /// <returns></returns> string introduceA(); } /// <summary> /// 产品B接口 /// </summary> public interface IProductB { /// <summary> /// 介绍产品B(方便测试) /// </summary> /// <returns></returns> string introduceB(); }
接口工厂
public interface ICreater { IProductA ProductADal { get; } IProductB ProductBDal { get; } }
public static class CreaterManager { //数据库访问实例 private static ICreater _instance; /// <summary> /// 获取实例 /// </summary> /// <returns></returns> public static ICreater GetIntance() { if (_instance == null) { //通过webConfig配置反射 string path = ConfigurationManager.AppSettings["DalPath"]; string value = ConfigurationManager.AppSettings["DalValue"]; Type type = Assembly.Load(path).GetType(path + "." + value); ConstructorInfo ci = type.GetConstructor(System.Type.EmptyTypes); _instance = (ICreater)ci.Invoke(null); } return _instance; } }
这样我们的接口层就基本完毕了,目录层次如下
mongodb数据访问的具体工厂
namespace MT.Abstract.MongodbDal { public class Creater : ICreater { /// <summary> /// 获取具体产品A实例 /// </summary> public IProductA ProductADal { get { return new ProductADal(); } } /// <summary> /// 获取具体产品B实例 /// </summary> public IProductB ProductBDal { get { return new ProductBDal(); } } } }
slqserver数据访问的具体工厂
namespace MT.Abstract.SqlserverDal { public class Creater : ICreater { /// <summary> /// 具体产品A /// </summary> public IProductA ProductADal { get { return new ProductADal(); } } /// <summary> /// 具体产品B /// </summary> public IProductB ProductBDal { get { return new ProductBDal(); } } } }
我们拿产品A来举例,分别展示mongodbDal产品A具体实现和SqlserverDal产品具体实现
namespace MT.Abstract.MongodbDal { public class ProductADal : IProductA { /// <summary> /// 具体实现 /// </summary> /// <returns></returns> public string introduceA() { return "mongodb_ProductA"; } } } namespace MT.Abstract.SqlserverDal { public class ProductADal : IProductA { /// <summary> /// 具体实现 /// </summary> /// <returns></returns> public string introduceA() { return "sqlserver_ProductA"; } } }
新增页面逻辑测试代码,为了方便我直接创建了一个空的webForm程序
public partial class Index : System.Web.UI.Page { /// <summary> /// access controller /// </summary> public ICreater _instance { get { return CreaterManager.GetIntance(); } } protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { string resultA = _instance.ProductADal.introduceA(); string resultB = _instance.ProductBDal.introduceB(); Response.Write("resultA:" + resultA + "_resultB:" + resultB); } } }
修改webconfig配置文件,我们先来测试sqlserver
<appSettings> <add key="DalPath" value="MT.Abstract.SqlserverDal"/> <add key="DalValue" value="Creater"/> </appSettings>
下面运行我们来看结果
再次修改webconfig,测试将数据驱动改为mongodb
<appSettings> <add key="DalPath" value="MT.Abstract.MongodbDal"/> <add key="DalValue" value="Creater"/> </appSettings>
运行结果
我们这个例子主要的关键点在于 ICreater 和具体工厂 Sqlsrever Creater 和 Mongodb Creater 来体验抽象工厂,这个例子是用反射+抽象工厂来实现的。也就是我们经常用到的通过配置切换DB。
1、抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。
2、当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
3、增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。
代码链接 : BlogAbstract