【设计模式】抽象工厂模式 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)
问题: 使用工厂方法模式的主要问题是工厂类过多,每个产品对应一个工厂,不利于维护.因此可以考虑使用一个工厂创建一个产品族. 定义: 提供创建一些列相关或相互依赖的对象实例的接口,这些类可以称为一个产品 ...
随机推荐
- HttpServletRequest 接口、HttpServletResponse 接口、请求转发与重定向
上篇文章我们讲了servlet的基本原理,这章将讲一下剩余的部分. HttpServletRequest 接口 该接口是 ServletRequest 接口的子接口,封装了 HTTP 请求的相关信息, ...
- spring boot整合mybatis方式一
方式一: 导入maven依赖: <!--web依赖配置--> <dependency> <groupId>org.springframework.boot</ ...
- sqlserver笔记----创建用户赋予权限
1.创建用户: create login username with password='密码' , default_database=数据库; create user username for lo ...
- Loj #2192. 「SHOI2014」概率充电器
Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...
- FFmpeg命令行工具学习(五):FFmpeg 调整音视频播放速度
FFmpeg对音频.视频播放速度的调整的原理不一样.下面简单的说一下各自的原理及实现方式: 一.调整视频速率 调整视频速率的原理为:修改视频的pts,dts 实现: ffmpeg -i input.m ...
- 解决MUI阻止a标签默认跳转事件—方法总结
用过mui的小伙伴们一定不会陌生,有时候真的很烦mui本身会阻止a标签默认跳转.一般只要用了mui的ui组件,比如头部,底部或者弹框,你就不能在用a标签进行跳转了. 注:项目中引用了mui后,可能也会 ...
- shell if条件判断中:双中括号与单中括号的区别
电脑重装了系统,登录虚拟机的shell脚本需重写,在为编写的脚本命名时发现存在同名脚本,才想起来是连接公司服务器的登录脚本,不想写俩脚本,怕记混了,那就整合一下.代码如下: #!/bin/bash#z ...
- Java数据结构和算法 - 栈和队列
Q: 栈.队列与数组的区别? A: 本篇主要涉及三种数据存储类型:栈.队列和优先级队列,它与数组主要有如下三个区别: A: (一)程序员工具 数组和其他的结构(栈.队列.链表.树等等)都适用于数据库应 ...
- 【深度学习篇】--Seq2Seq模型从初识到应用
一.前述 架构: 问题: 1.压缩会损失信息 2.长度会影响准确率 解决办法: Attention机制:聚焦模式 “高分辨率”聚焦在图片的某个特定区域并以“低分辨率”,感知图像的周边区域的模式.通过大 ...
- ASP.NET Core MVC应用程序中的后台工作任务
在应用程序的内存中缓存常见数据(如查找)可以显着提高您的MVC Web应用程序性能和响应时间.当然,这些数据必须定期刷新. 当然你可以使用任何方法来更新数据,例如Redis中就提供了设定缓存对象的生命 ...