Abstract Factory 抽象工厂(创建型模式)
1、常规的对象创建方法(以更换QQ空间主题为例)
(这里的常规对象指的是由于业务需求,当前实例化的对象有可能被其他相似的对象(有着共同的特性)所取代,例如更换手机铃声:一首歌取代另一首歌(词,曲,和声是他们的共同有的特性(这里说的词,曲,和声是当前实例化对象的属性,如果是对象的话,那我们就要使用抽象工厂了,第4会开始讲),只是词,曲的风格是不一样的!))
ModernTheme theme=new ModernTheme();//现代主题
常规的对象创建方法引发的问题是:当我们用户这时需要更换成经典主题(ClassicTheme),那我们程序中用到ModernTheme的地方,都要重新实例化成ClassicTheme了,那么每一次更换主题,都要更换程序代码,代码的更改量将会很大!也不便于程序维护!
2、常规对象创建的解决思路
- 封装变化点 —— 哪里变化,封装哪里
- 潜台词:如果没有变化,当然不需要额外的封装!
- 应该遵循“开闭原则”。即:封闭对原来代码的修改,开放对原来代码的扩展(如类的继承,接口的实现)
3、常规对象创建的解决方法
为能够相互取代的对象都实现同一接口:
那我们代码就可以变为
但是还是无法解决问题的,标号为2的地方我们还是需要更改的,这里我们可以这样解决, 利用配置xml文件进行发射实例化具体的主题类
xml文件:
<?xml version="1.0" encoding="utf-8" ?>
<ThemeSettings>
<Theme key="ThemeName" value="ModernTheme"></Theme>
</ThemeSettings>
实例化对象的代码就变为:
XmlDocument document = new XmlDocument();
document.Load("F:\\Theme.xml");
XmlNode nodes = document.SelectSingleNode("ThemeSettings");
string key = nodes.SelectSingleNode("Theme").Attributes["value"].Value;//获得要实例化的对象名称
Assembly assembly = Assembly.LoadFile("F:\\ModernTheme\\ModernTheme.dll");//加载现代主题Dll
Type t = assembly.GetExportedTypes()[];
ITheme theme = (ITheme)Activator.CreateInstance(t); //实例化具体的主题对象
8 //这里的反射我们可以通过缓存进行处理,如果缓存中有此对象,就从缓存中取,如果每次都进行反射!会影响程序的效率!
这样一写,我们就不需要更改程序代码了,只需要配置xml文件即可!从而达到了对修改关闭的效果了!
说了这么多,有的人会认为我上面所说的都是和抽象工厂无关的!都是些废话,但对于我而言不是这么认为的,只是为讲解抽象工厂做的铺垫!
4、引出抽象工厂
当我们想创建一系列相互依赖的对象时,我们该怎么办?
一系列相互依赖的对象:例如更换QQ空间主题,主题类下面的有导航,装饰,播放器,动画,皮肤等一系列相互以来的对象,每一个对象的位置都影响其他对象的位置,每一个对象的皮肤颜色都影响这其他对象皮肤的颜色(一个主题基本色调要确定)!
假如我现在想更换成非主流主题,那么这个非主流主题就对应着常规对象创建中的ModernTheme,又想换成欧式风格主题,那么就对应着常规对象创建中的ClassicTheme,不同的是:一系列对象的创建,他的成员是多个相互依赖的对象,而常规对象的创建,他的成员是多个属性或者字段,不是对象!
如果我们按照常规对象创建的方法来应用到创建一系列对象上去的话,那么应该代码为:
//非主流主题
//播放器
XmlDocument document = new XmlDocument();
document.Load("F:\\Theme.xml");
XmlNode nodes = document.SelectSingleNode("PlayerSettings");
string key = nodes.SelectSingleNode("Player").Attributes["value"].Value;
Assembly assembly = Assembly.LoadFile("F:\\ModernTheme\\bin\\Debug\\AlternativeTheme.dll");
Type t = assembly.GetExportedTypes()[];
IPlayer theme = (IPlayer)Activator.CreateInstance(t);
//导航
document.Load("F:\\Theme.xml");
XmlNode nodes = document.SelectSingleNode("NavigationSettings");
string key = nodes.SelectSingleNode("Navigation").Attributes["value"].Value;
Assembly assembly = Assembly.LoadFile("F:\\ModernTheme\\bin\\Debug\\AlternativeTheme.dll");
Type t = assembly.GetExportedTypes()[];
INavigation theme = (INavigation)Activator.CreateInstance(t);
//装饰
document.Load("F:\\Theme.xml");
XmlNode nodes = document.SelectSingleNode("DecorateSettings");
string key = nodes.SelectSingleNode("Decorate").Attributes["value"].Value;
Assembly assembly = Assembly.LoadFile("F:\\ModernTheme\\bin\\Debug\\AlternativeTheme.dll");
Type t = assembly.GetExportedTypes()[];
IDecorate theme = (IDecorate)Activator.CreateInstance(t);
这时我们想换成非主流主题的话,那么就需要去更改配置文件下的播放器,导航,装饰的代码了,修改成非主流播放器,非主流导航,非主流装饰,每次更换一下主题,都要更换xml下这三个对象的值
缺点:1、多次运用反射,效率降低
2、对象之间的依赖关系没有体现
3、容易出错,xml文件容易配置出错!
4、代码冗余,不够直观的表达出更换主题的意图
运用抽象工厂,"工厂"顾名思义就是创建一系列对象的地方,我们可以把对播放器、导航、装饰着三个对象放在抽象工厂里面去创建,我们叫工厂创建非主流风格的QQ空间主题,那么工厂内部就会帮我们创建好带有非主流风格的播放器,导航,装饰,然后根据三者的依赖关系进行组合,从而形成非主流QQ空间主题!
既然我们有时候会向工厂要非主流QQ空间主题,有时候也会向工厂要欧式QQ空间主题或者其他主题,这时我们就可以利用接口来应变这种需求!
代码实现为:
abstract ThemeAbstractFactory
{
public abstract Player CreatePlayer(); //Player为抽象类
public abstract Navigation CreateNavigation();//Navigation为抽象类
public abstract Decorate CreateDecorate();//Decorate为抽象类
}
//非主流主题
class AlternativeTheme : ThemeAbstractFactory
{
public override Player CreatePlayer()
{
return new AlternativeRoad();
}
public override Navigation CreateNavigation()
{
return new AlternativeRoad();
}
public override Decorate CreateDecorate()
{
return new AlternativeRoad();
}
}
class ThemeManager
{
ThemeAbstractFactory themeFactory;
public ThemeManager(ThemeAbstractFactory themeFactory)
{
this.themeFactory = themeFactory;
}
//创建主题下各个对象
public void BuildThemeObject()
{
Player player = themeFactory.CreatePlayer();
Navigation navigation = themeFactory.CreateNavigation();
Decorate decorate = themeFactory.CreateDecorate();
//实现三者依赖的代码实现
}
}
客户端代码为:
XmlDocument document = new XmlDocument();
document.Load("F:\\Theme.xml");
XmlNode nodes = document.SelectSingleNode("ThemeSettings");
string key = nodes.SelectSingleNode("Theme").Attributes["value"].Value;
Assembly assembly = Assembly.LoadFile("F:\\AlternativeTheme \\AlternativeTheme .dll");
Type t = assembly.GetExportedTypes()[];
7 ThemeAbstractFactory theme = (ThemeAbstractFactory)Activator.CreateInstance(t);
ThemeManager manager = new ThemeManager(theme);
因为更换主题,无非是更换这三者类型的值,所以我们就可以把三者抽象出来,称为Player,Navigation,Decorate三个抽象类
无论我们怎样更换主题,ThemeManager主题管理类是不需要变的,运用了抽象与接口的相结合,达到了对修改关闭的效果,
每次更换主题我们就可以修改下配置文件即可!代码量远远减少,可读性强!
5、整体结构:
6、应对“新对象”产生的问题
抽象工厂模式主要在于应对“新系列”的需求变化。其缺点在于难于应付“新对象”的需求变动,如果非主流主题下需要一个动画对象,那么ThemeManager、ThemAbstractFactory都需要跟着变,这就违反了设计模式的原则了!如果在开发中出现了新对象,该如何去解决呢?这个问题并没有一个好的答案,下面我们看一下李建忠老师的回答:“GOF《设计模式》中提出过一种解决方法,即给创建对象的操作增加参数,但这种做法并不能令人满意。事实上,对于新系列加新对象,就我所知,目前还没有完美的做法,只有一些演化的思路,这种变化实在是太剧烈了,因为系统对于新的对象是完全陌生的。“
如果你的项目中,对象的变更是很长一段时间才变更,那么适合使用抽象工厂模式,如果是经常变更的话,就不适合使用抽象工厂模式了!
7、实现要点
- 抽象工厂将产品对象的创建延迟到它的具体工厂的子类。
- 如果没有应对“多系列对象创建”的需求变化,则没有必要使用抽象工厂模式,这时候使用简单的静态工厂完全可以。
- 系列对象指的是这些对象之间有相互依赖、或作用的关系,例如游戏开发场景中的“道路”与“房屋”的依赖,“道路”与“地道”的依赖。
- 抽象工厂模式经常和工厂方法模式共同组合来应对“对象创建”的需求变化。
- 通常在运行时刻创建一个具体工厂类的实例,这一具体工厂的创建具有特定实现的产品对象,为创建不同的产品对象,客户应使用不同的具体工厂。
- 把工厂作为单件,一个应用中一般每个产品系列只需一个具体工厂的实例,因此,工厂通常最好实现为一个单件模式。
- 创建产品,抽象工厂仅声明一个创建产品的接口,真正创建产品是由具体产品类创建的,最通常的一个办法是为每一个产品定义一个工厂方法,一个具体的工厂将为每个产品重定义该工厂方法以指定产品,虽然这样的实现很简单,但它确要求每个产品系列都要有一个新的具体工厂子类,即使这些产品系列的差别很小。
8、优点
- 分离了具体的类。抽象工厂模式帮助你控制一个应用创建的对象的类,因为一个工厂封装创建产品对象的责任和过程。它将客户和类的实现分离,客户通过他们的抽象接口操纵实例,产品的类名也在具体工厂的实现中被分离,它们不出现在客户代码中。
- 它使得易于交换产品系列。一个具体工厂类在一个应用中仅出现一次——即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变。
- 它有利于产品的一致性。当一个系列的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要,而抽象工厂很容易实现这一点。
9、缺点
难以支持新种类的产品。难以扩展抽象工厂以生产新种类的产品。这是因为抽象工厂接口确定了可以被创建的产品集合,支持新种类的产品就需要扩展该工厂接口,这将涉及抽象工厂类及其所有子类的改变。
Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
10、适用性
在以下情况下应当考虑使用抽象工厂模式:
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
- 这个系统有多于一个的产品族,而系统只消费其中某一产品族。
- 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
11、应用场景
- 支持多种观感标准的用户界面工具箱(Kit)。
- 游戏开发中的多风格系列场景,比如道路,房屋,管道等。
- ……
12、总结
总之,抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口,运用抽象工厂模式的关键点在于应对“多系列对象创建”的需求变化。
13、参考
《C#面向对象设计模式纵横谈》 李建忠
http://terrylee.cnblogs.com TerryLee
作者:MrZivChu
2013-08-05 12:57:36
Abstract Factory 抽象工厂(创建型模式)的更多相关文章
- C#面向对象设计模式纵横谈——3.Abstract Factory 抽象工厂(创建型模式)
动机(Motivation) 在软件系统中经常面临着“一系列相互依赖的对象”的创建工作,同时,由于需求变化,往往存在更多系列对象的创建工作.如何应对这种变化?如何绕过常规对象的创建,提供一种“封装机制 ...
- [C#]设计模式-抽象工厂-创建型模式
介绍了简单工厂与工厂方法之后,现在我们来看一下工厂三兄弟的最后一个 -- 抽象工厂. 那什么是抽象工厂呢? 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相 ...
- 设计模式02: Abstract Factory 抽象工厂(创建型模式)
Abstract Factory 抽象工厂(创建型模式) 常见的对象创建方法: //创建一个Road对象 Road road=new Road(); new的问题: -实现依赖 ...
- 对象创建型模式------Abstract Factory(抽象工厂)
1. 意图 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类.2. 别名 Kit3. 动机 假设我们要开发一款游戏,当然为了吸引更多的人玩,游戏难度不能太大 ...
- Abstract Factory抽象工厂模式
抽象工厂模式是是用一个超级工厂去创建其他工厂,简单点说就是工厂的父类,属于创建型模式. 目标:提供一个创建一组对象的方法,而无需指定它们具体的类(同工厂方法). 使用场景:系统的产品有多于一个的产品族 ...
- Java设计模式:Abstract Factory(抽象工厂)模式
概念定义 抽象工厂(Abstract Factory)模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 抽象工厂模式中,系统的产品有多于一个的产品族(一个产品族里定义多个产品) ...
- Abstract Factory 抽象工厂
意图 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 适用性 一个系统要独立于它的产品的创建.组合和表示时 结构 参与者 AbstractFactory:声明一个创建抽象产品对象 ...
- 设计模式(一): abstract factory抽象工厂模式 -- 创建型模式
1.定义 为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类. 2.适用场景 1.一个系统要独立于它的产品创建.组合和表示. 2.一个系统要由多个产品系列中的一个来配置. 3.当你要 ...
- Abstract factory抽象工厂--对象创建型
意图: 提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类. 别名:Kit 补充: 抽象产品A : (产品A1 和产品 A2) 抽象产品B : ( 产品B1 和 产品B2) 一般情况 ...
随机推荐
- Bean的初始化和销毁
在我们实际开发的时候,经常会遇到在Bean在使用之前或者之后做些必要的操作,Spring对Bean的生命周期的操作提供了支持.在使用Java配置和注解配置下提供如下两种方式. 1.Java配置方 ...
- webapi中配置返回的时间数据格式
web api返回的是标准格式UTC时间,如果要转成我们需要的格式,可以在WebApiConfig.cs的Register函数中新增以下配置来定义返回的时间类型格式: //配置返回的时间类型数据格式 ...
- 第一个AngularJS控制器
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- js动画效果
js能够按照预定的时间间隔重复调用一个函数,而这意味着我们可以随着时间的推移而不断改变某个元素的样式.动画是样式随时间变化的完美例子之一. js函数setTimeout能够让某个函数在经过一段预定的时 ...
- JavaScript中的事件循环
JavaScript是单线程单并发语言 单线程:主程序只有一个线程,即同一时间片段内其只能执行单个任务. 引发的问题: 单线程,意味着任务都需要排队,前一个任务结束,才会执行后一个任务.若前一个任务耗 ...
- .NET向WebService传值为decimal、double、int、DateTime等非string类型属性时,服务器端接收不到数据的问题
最近在做CRM项目时,使用C#调用SAP PI发布的WebService服务时遇到的问题: 向WebService传值为decimal.double.int.DateTime等非string类型数据时 ...
- 前端之jquery函数库
jquery介绍 jQuery是目前使用最广泛的javascript函数库.据统计,全世界排名前100万的网站,有46%使用jQuery,远远超过其他库.微软公司甚至把jQuery作为他们的官方库. ...
- 交换机基础配置之三层交换机实现vlan间通信
我们以上面的拓扑图做实验,要求为pc1,pc2,pc3配置为vlan10,pc4,pc5,pc6配置为vlan20,pc7,pc8,pc9配置为vlan30 server0和server1配置为vla ...
- IntelliJ IDEA 12详细开发教程(一)思想的转变与新手入门【转】
转载地址:http://bangqu.com/alicas/blog/433 从事软件开发工作以来,提高自己的开发效率,提高自己编码的规范,提高编码深度层次,这三样一直都是自己努力去追求的事情. 最近 ...
- Orcale(一)
oracle数据库基本语句查询 ORacle-12560:TNS 协议配置器错误 1.服务:1.监听服务未开启 2.服务器未开启 3.环境变量:Oracle_sid = orcl 4.regedit注 ...