【设计模式】抽象工厂模式 Abstract Factory Pattern
简单工厂模式是一个工厂类根据工厂方法的参数创建不出不同的产品, 工厂方法模式是每一个产品都有一个一一对应的工厂负责创建该产品。那么今天要讲的抽象工厂模式是一个工厂能够产生关联的一系列产品。抽象工厂模式相对于简单工厂和工厂方法模式来着更具抽象性。
一、抽象工厂模式演绎
我们先来看一个简单的需求: 甲方要开发一套办公自动化软件,其中有一个非常重要的功能就是要能够导入Word 文档和Excel 文档。
开发人员拿到需求后就开始编码了, 很快代码写完了:
public class ImportTool
{
public void ImportWord()
{
Console.WriteLine("Import Word");
} public void ImportExcel()
{
Console.WriteLine("Import Excel");
}
}
客户端调用代码:
class Program
{
static void Main(string[] args)
{
ImportTool importTool = new ImportTool();
importTool.ImportWord();
importTool.ImportExcel(); Console.ReadKey();
}
}
输出结果:
看起来不错, 但是看代码,客户端的代码和具体的实现之间是直接new出来的 ,简直就是面向具体编程,没有接口没有抽象,是不是违背了ISP原则了?,那好开发人员决定提出一个抽象层,提出一个抽象的文档接口, 用简单工厂模式来实现这个需求:
UML 图如下:
代码如下:
public interface IDocument
{
void Import();
}
public class WordDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import Word");
}
}
public class ExcelDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import Excel");
}
} public class DocumentFactory
{
public static IDocument Create(string documentType)
{
IDocument document;
switch (documentType)
{
case "word":
document = new WordDocument();
break;
case "excel":
document = new ExcelDocument();
break;
default:
throw new ArgumentException("Invalid argument: documentType");
} return document;
}
}
客户端调用:
static void Main(string[] args)
{
IDocument document=DocumentFactory.Create("word");
document.Import(); document=DocumentFactory.Create("excel");
document.Import(); Console.ReadKey();
}
输出:
看起来完美这是一个标准的静态工厂模式的实现。
第一次需求变更: 增加 对Power Point 的导入支持
增加了一个产品相当于,因为之前应用了简单工厂模式现在改起来很简单。
UML 图:
代码:
public interface IDocument
{
void Import();
}
public class WordDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import Word");
}
}
public class ExcelDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import Excel");
}
}
public class PowerPointDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import Power Point");
}
} public class DocumentFactory
{
public static IDocument Create(string documentType)
{
IDocument document;
switch (documentType)
{
case "word":
document = new WordDocument();
break;
case "excel":
document = new ExcelDocument();
break;
case "powerpoint":
document = new PowerPointDocument();
break;
default:
throw new ArgumentException("Invalid argument: documentType");
} return document;
}
}
客户端调用:
static void Main(string[] args)
{
IDocument document=DocumentFactory.Create("word");
document.Import(); document=DocumentFactory.Create("excel");
document.Import(); document = DocumentFactory.Create("powerpoint");
document.Import();
Console.ReadKey();
}
输出结果:
没问题,一起都在控制之中。
第二次需求变更: 支持office 2007 以后的文档格式。
Offcie 2007是个坎,2007之前的文档格式和2007以后的文档格式不一 样, word 在2007前的文档后缀是.doc, 2007 及以后就变成.docx了这个看似简单的需求实则是增加了小一半的工作量啊,新的格式的文档要重新进行解码才能拿到正确的数据,拿刚刚实现的简单工厂设计模式来应对这次变更就要新增加3个产品类并且要工厂来创建创建者三个类的实例。看来静态工厂已经不能再适应这一次的需求变化了,会导致静态工厂方法的逻辑变得异常复杂难以维护。那用工厂方法模式来解决这个问题,工厂方法刚好可以将产品的创建工作提取到单独的工厂中去完成,重构下工厂将静态工厂模式替换成工厂方法模式:
UML 图:
代码:
public interface IDocument
{
void Import();
}
public interface IDocumentFactory
{
IDocument Create();
}
public class WordDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import Word");
}
}
public class ExcelDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import Excel");
}
}
public class PowerPointDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import Power Point");
}
} public class WordXDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import WordX");
}
}
public class ExcelXDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import ExcelX");
}
}
public class PowerPointXDocument : IDocument
{
public void Import()
{
Console.WriteLine("Import Power PointX");
}
} public class WordDocumentFactory : IDocumentFactory
{
public IDocument Create()
{
return new WordDocument();
}
}
public class WordXDocumentFactory : IDocumentFactory
{
public IDocument Create()
{
return new WordXDocument();
}
} public class ExcelDocumentFactory : IDocumentFactory
{
public IDocument Create()
{
return new ExcelDocument();
}
}
public class ExcelXDocumentFactory : IDocumentFactory
{
public IDocument Create()
{
return new ExcelXDocument();
}
}
public class PowerPointDocumentFactory : IDocumentFactory
{
public IDocument Create()
{
return new PowerPointDocument();
}
}
public class PowerPointXDocumentFactory : IDocumentFactory
{
public IDocument Create()
{
return new PowerPointXDocument();
}
}
客户端调用代码:
static void Main(string[] args)
{
IDocument document;
IDocumentFactory documentFactory; documentFactory = new WordDocumentFactory();
document = documentFactory.Create();
document.Import(); documentFactory = new WordXDocumentFactory();
document = documentFactory.Create();
document.Import(); documentFactory = new ExcelDocumentFactory();
document = documentFactory.Create();
document.Import(); documentFactory = new ExcelXDocumentFactory();
document = documentFactory.Create();
document.Import(); documentFactory = new PowerPointDocumentFactory();
document = documentFactory.Create();
document.Import(); documentFactory = new PowerPointXDocumentFactory();
document = documentFactory.Create();
document.Import(); Console.ReadKey();
}
输出结果:
一个产品一个实现类,一个工厂类,这样职责单一符合SRP,但是系统中的类在成倍的增加,有点复杂了,如果在增加一个系列的产品那还了得。
那么能不能减少一些类呢?经过分析我们发现,这些导入的文档中2007之前的一系列文档的解析规则基本类似实现的技术也是类似的,2007及以后的文档的解析规则类似。所以我们可以把这些产品分成两个系列,2007之前的成为Document系列,2007以后的文档成为DocumentX系列, 那么我们就可以创建两个具体的工厂来创建Document系列和DocumentX系列, 从另一个维度来看,Word 和WordX, Excel 和ExcelX,PowerPoint 和 PowerPointX的的关系也很密切,因为都是同一个产品,只是处在不同的系列上,他们各自的编码又各自类似,因此在这个维度上可以将其提出一组新的接口,IWordDocument 用于处理word的导入(Word和WordX),IExcelDocument 用处理Excel的导入(Excel 和ExcelX),IPowerPoint用于处理 PowerPoint 导入(PowerPoint和PowerPointX),根据这个思路重构代码:
UML 图
代码:
public interface IWordDocument
{
void Import();
}
public interface IExcelDocument
{
void Import();
}
public interface IPowerPointDocument
{
void Import();
}
public interface IDocumentFactory
{
IWordDocument CreateWord();
IExcelDocument CreateExcel();
IPowerPointDocument CreatePowerPoint();
}
public class WordDocument : IWordDocument
{
public void Import()
{
Console.WriteLine("Import Word");
}
}
public class WordXDocument : IWordDocument
{
public void Import()
{
Console.WriteLine("Import WordX");
}
}
public class ExcelDocument : IExcelDocument
{
public void Import()
{
Console.WriteLine("Import Excel");
}
}
public class ExcelXDocument : IExcelDocument
{
public void Import()
{
Console.WriteLine("Import ExcelX");
}
}
public class PowerPointDocument : IPowerPointDocument
{
public void Import()
{
Console.WriteLine("Import Power Point");
}
}
public class PowerPointXDocument : IPowerPointDocument
{
public void Import()
{
Console.WriteLine("Import Power PointX");
}
}
public class DocumentFactory : IDocumentFactory
{
public IWordDocument CreateWord()
{
return new WordDocument();
} public IExcelDocument CreateExcel()
{
return new ExcelDocument();
} public IPowerPointDocument CreatePowerPoint()
{
return new PowerPointDocument();
}
}
public class DocumentXFactory : IDocumentFactory
{
public IWordDocument CreateWord()
{
return new WordXDocument();
} public IExcelDocument CreateExcel()
{
return new ExcelXDocument();
} public IPowerPointDocument CreatePowerPoint()
{
return new PowerPointXDocument();
}
}
客户端调用:
static void Main(string[] args)
{
IWordDocument wordDocument;
IExcelDocument excelDocument;
IPowerPointDocument powerPointDocument; IDocumentFactory documentFactory; documentFactory = new DocumentFactory();
wordDocument = documentFactory.CreateWord();
excelDocument = documentFactory.CreateExcel();
powerPointDocument = documentFactory.CreatePowerPoint();
wordDocument.Import();
excelDocument.Import();
powerPointDocument.Import(); documentFactory = new DocumentXFactory();
wordDocument = documentFactory.CreateWord();
excelDocument = documentFactory.CreateExcel();
powerPointDocument = documentFactory.CreatePowerPoint();
wordDocument.Import();
excelDocument.Import();
powerPointDocument.Import(); Console.ReadKey();
}
输出:
这一次工厂的数量得到了控制,这里只有两个工厂类就搞定了,DocumentFactory 负责创建office 2007之前的文档对象,DocumentXFactory 负责创建 office 2007以及以后的文档对象。这就解决了工厂方法模式工厂类随着产品增加随之增加带来的复杂性,以及不易维护的问题。这样如果在增加一系列产品就变得容易了很多,只需要再创建一个系列产品的具体工厂并继承自抽象工厂,以及实现系列产品的抽象接口的具体类就可以了。
随着需求的变化一步一步的经过重构代码也一步一步从简单工厂模式到工厂方法模式再到抽象工厂模式了。现在文档处理代码就是一个典型的抽象工厂模式了,那么下面来看看抽象工厂模式到底是什么?
二、抽象工厂模式定义:
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族(产品系列).
三、工厂方法模式结构图:
抽象工厂模式结构图
1、AbsctactFactory(抽象抽象工厂):
它声明了一组用于创建一系列产品的方法,每一个方法对应创建一种产品。
2.ConcreteFactory(具体工厂):
它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品系列,每个产品都在同一个系列中。
3. AbsctactProdcut(抽象产品):
它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
4.ConcreteProdcut(具体产品):
它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。
四、抽象工厂模式代码实现
在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可以是抽象类或者具体类,其典型代码如下:
public interface IAbstractProdcutA
{
void DoSomething();
}
public interface IAbstractProdcutB
{
void DoSomething();
}
public interface IAbstractFactory{
IAbstractProdcutA CreateProductA();
IAbstractProdcutB CreateProductB();
} public class ConcreteProductA1:IAbstractProdcutA
{
public void DoSomething()
{
Console.WriteLine("I'm ConcreteProductA1");
}
}
public class ConcreteProductA2:IAbstractProdcutA
{
public void DoSomething()
{
Console.WriteLine("I'm ConcreteProductA2");
}
} public class ConcreteProductB1:IAbstractProdcutB
{
public void DoSomething()
{
Console.WriteLine("I'm ConcreteProductB1");
}
}
public class ConcreteProductB2:IAbstractProdcutB
{
public void DoSomething()
{
Console.WriteLine("I'm ConcreteProductB2");
}
} public class ConcreteFactory1 : IAbstractFactory
{
public IAbstractProdcutA CreateProductA()
{
return new ConcreteProductA1();
}
public IAbstractProdcutB CreateProductB()
{
return new ConcreteProductB1();
}
} public class ConcreteFactory2 : IAbstractFactory
{
public IAbstractProdcutA CreateProductA()
{
return new ConcreteProductA2();
}
public IAbstractProdcutB CreateProductB()
{
return new ConcreteProductB2();
}
}
客户端调用代码:
static void Main(string[] args)
{
IAbstractProdcutA productA;
IAbstractProdcutB productB; IAbstractFactory factory = new ConcreteFactory1();
productA = factory.CreateProductA();
productB = factory.CreateProductB();
productA.DoSomething();
productB.DoSomething(); factory = new ConcreteFactory2();
productA = factory.CreateProductA();
productB = factory.CreateProductB() ;
productA.DoSomething();
productB.DoSomething(); Console.ReadKey();
}
输出:
五 、抽象工厂模式的优点
抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。比如常用的配置+反射就可轻松替换掉工厂进而替换掉整个以为逻辑。
当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品系列中的对象。
增加新的产品系列很方便,无须修改已有系统,只需要增加具体产品类和具体工厂就可以了符合“开闭原则(OCP)“。
六、抽象工厂模式的缺点
- 增加新的产品麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则(OCP)“。如果新增一个产品就要在抽象工厂中增加一个创建该产品的方法。这样就牵一发动全身,原来的所有集成该抽象工厂的具体工厂都要修改。
七、抽象工厂模式的使用场景
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
- 系统中有多于一个的产品系列,而每次只使用其中某一产品系列。可以通过配置文件等方式来使得用户可以动态改变产品系列,也可以很方便地增加新的产品系列。
- 属于同一个产品系列的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品系列中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如Office 文档的支持,在window xp 重视不能使用Office 2007 +的,window 7 支持0ffice 2007,因此这个约束就是操作系统对office版本的支持。
- 产品类型稳定,设计完成之后,不会向系统中增加新的产品类型或者删除已有的产品产品类型。
八、扩展
第三次需求改变:从商业化上考虑甲方要求这个软件的某些版本只提供导入office 2007 之前的文件,某些版本只支持2007以后版本。
怎么办呢? 这个需求开发人员拿到后一定窃喜,抽象工厂实现这种需求简直是太容易了。配置+反射呀, 将具体工厂配置在配置文件中,代码中只是用抽象并通过反射来创建工厂实进而达到动态控制的能力。
如果软件版本中V1中只要支持office 2007以前的文档版本。那么在配置文件App.config 增加以下节点:
配置完节点后在代码中通过反射来创建工厂类, 代码如下
static void Main(string[] args)
{
IWordDocument wordDocument;
IExcelDocument excelDocument;
IPowerPointDocument powerPointDocument; IDocumentFactory documentFactory; // 读取配置文件并且通过反射创建工厂实例
var setting = ConfigurationSettings.AppSettings["DocumentFaccory"];
var obj = Type.GetType(setting);
if (obj == null) return;
documentFactory = Activator.CreateInstance(obj) as IDocumentFactory; if (documentFactory == null) return; wordDocument = documentFactory.CreateWord();
excelDocument = documentFactory.CreateExcel();
powerPointDocument = documentFactory.CreatePowerPoint();
wordDocument.Import();
excelDocument.Import();
powerPointDocument.Import(); Console.ReadKey();
}
输出结果如下:
如果在V1.1版本中甲方提出要求只支持office 2007以后的版本,那么只需要修改配置文件如下就可以了:
把原来的
<appSettings>
<add key="DocumentFaccory" value="DesignPattern.AbstractFactory.DocumentFactory"/>
</appSettings>
改成:
<appSettings>
<add key="DocumentFaccory" value="DesignPattern.AbstractFactory.DocumentXFactory"/>
</appSettings>
其它地方不用做任何修改,输出结果:
【设计模式】抽象工厂模式 Abstract Factory Pattern的更多相关文章
- 设计模式 - 抽象工厂模式(abstract factory pattern) 具体解释
抽象工厂模式(abstract factory pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/2709 ...
- C#设计模式——抽象工厂模式(Abstract Factory Pattern)
一.概述在软件开发中,常常会需要创建一系列相互依赖的对象,同时,由于需求的变化,往往存在较多系列对象的创建工作.如果采用常规的创建方法(new),会造成客户程序和对象创建工作的紧耦合.对此,抽象工厂模 ...
- 乐在其中设计模式(C#) - 抽象工厂模式(Abstract Factory Pattern)
原文:乐在其中设计模式(C#) - 抽象工厂模式(Abstract Factory Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 抽象工厂模式(Abstract Factor ...
- 二十四种设计模式:抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式(Abstract Factory Pattern) 介绍提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 示例有Message和MessageModel,Messag ...
- 【UE4 设计模式】抽象工厂模式 Abstract Factory Pattern
概述 描述 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类:具体的工厂负责实现具体的产品实例 抽象工厂中每个工厂可以创建多种产品(如苹果公司生产iPhone.iPad): 工厂方法 ...
- 设计模式之抽象工厂模式(Abstract Factory Pattern)
一.抽象工厂模式的由来 抽象工厂模式,最开始是为了解决操作系统按钮和窗体风格,而产生的一种设计模式.例如:在windows系统中,我们要用windows设定的按钮和窗体,当我们切换Linux系统时,要 ...
- Net设计模式实例之抽象工厂模式(Abstract Factory Pattern)
一.抽象工厂模式简介(Bref Introduction) 抽象工厂模式(Abstract Factory Pattern),提供一个创建一系列相关或者相互依赖对象的接口,而无需制定他们的具体类.优点 ...
- Objective-C设计模式——抽象工厂模式Abstract Factory(对象创建)
抽象工厂模式 理解了工厂方法模式,其实抽象工厂和工厂方法模式有很多的相似之处.抽象工厂同样是分离客户端对象的创建和逻辑代码的,但是抽象工厂往往是产生一组数据而不单单是产生一个产品. 抽象工厂提供一个创 ...
- 六个创建模式之抽象工厂模式(Abstract Factory Pattern)
问题: 使用工厂方法模式的主要问题是工厂类过多,每个产品对应一个工厂,不利于维护.因此可以考虑使用一个工厂创建一个产品族. 定义: 提供创建一些列相关或相互依赖的对象实例的接口,这些类可以称为一个产品 ...
随机推荐
- .NET Core IdentityServer4实战 第三章-使用EntityFramework Core进行持久化配置
内容:本文带大家使用IdentityServer4进行使用使用EntityFramework Core进行配置和操作数据 作者:zara(张子浩) 欢迎分享,但需在文章鲜明处留下原文地址. 前两章内容 ...
- redis基本类型以及优点特性
1.什么是redis? redis是一个基于内存的高性能key-value数据库 2.redis基本数据类型及应用场景 支持多种数据类型: string(字符串) String数据结构是简单的k ...
- WebGL three.js学习笔记 自定义顶点建立几何体
自定义顶点建立几何体与克隆 Three.js本身已经有很多的网格模型,基本已经够我们的使用,但是如果我们还是想自己根据顶点坐标来建立几何模型的话,Three.js也是可以的. 基本效果如图: 点击查看 ...
- 【干货分享】可能是东半球最全的.NET Core跨平台微服务学习资源
如果你发现还有西半球的资源,烦请相告,不胜感谢! 一..NET Core基础 微软英文官网 .NET Core 微软中文官网 GitHub 用ASP.NET内核和Azure构建现代Web应用程序 博客 ...
- 6.使用桌面版AI伴侣或手机版AI伴侣实时预览编程效果
1.根据自己系统下载对应版本AI伴侣,最好解压到D盘根目录. 运行ailaunch.bat ,可以启动原本自带的2.46版AI伴侣.运行AI伴侣247.bat可以启动汉化2.47版的AI伴侣.并且默认 ...
- Appium的入门使用
ps:有没有人和我一样觉得Appium官方文档写的很烂的, 这官方文档,还不如很多人写的博客详细,而且对于初学的入门者实在是不够友好, 官网:https://github.com/appium/jav ...
- 【SQL Server】利用游标将学生表中的成绩转化为绩点
软件工程综合实践第一次作业 代码来源:班上同学的数据库大作业 alter table sc add GPA float; --加入绩点列 alter table sc ,);--将表按原始位置顺序编号 ...
- Fiddler-弱网测试设置
第一步:打开模拟弱网环境 第二步:打开配置文件 第三步:修改配置参数 m_SimulateModem,修改后最好 Ctrl+S 保存一下 第四步:修改好参数返回后需要再次打开弱网环境 以上弱网设置就 ...
- 【Android Studio安装部署系列】四十一、Android Studio安装Alibaba Java Coding Guidelines插件(检查代码规范)
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 该插件主要是对今年发布的<阿里巴巴 Java 开发规约>的一个延伸,它以一个 IDE 的插件存在,可以自动对手册中的 J ...
- 深入浅出mongoose
深入浅出mongoose mongoose是nodeJS提供连接 mongodb的一个库. 此外还有mongoskin, mongodb(mongodb官方出品). 本人,还是比较青睐mongoose ...