stand on the shoulders of giants

【设计模式】之 Simple Factory & Abstract Factory

简单工厂模式引子(从重构来理解)

思考:New存在问题么?
Sting str=new String() 我们平常都是这样用,怎么会有问题呢?

那我们再来看:
类库---
class Road
{
        
//
}
客户程序---
Road road 
= new Road();   
有问题么?应该没有吧...其实...
new确实存在问题:实现依赖,不能应对“具体实例化类型”的变化。
就是说Road()发生变化,比方说参数发生变化, 不仅要改Road类,客户程序Road road = new Road() 也需要更改

String类几乎不发生什么改变,是稳定的,就不存在这个问题

简单工厂模式又称为静态工厂模式:
封装变化点,哪里变化,封装哪里 没有变化就不用封装
---变化点在“对象创建” 所以封装对象创建, wrapper Road类

简单工厂模式 structural code

这种封装其实是把实例化提出到另一处地方,相当于过程式程序设计中的函数的概念。
面对新的需求变化,例如Road构造方法发生变化,简单工厂模式将变化隔离在类库中。
思考
:如果我们增加一种Bridge呢?在类库中增加Bridge类,CreateBridge方法。

简单工厂模式实例

简单工厂模式对程序的复用性,易维护,可扩展带来什么好处呢?看例子(来自cj723"大话设计模式")
假设"TO"公司做了一个Operation的类,封装了常用的计算方法。
//Operation.cs
  
---未使用工厂模式,我们直接拿来使用:
operation oper = new operationSub();
oper.NumberA = 56;
oper.NumberB = 8;
double c = oper.GetResult();
同样存在“实现依赖”的问题;
---使用简单工厂模式解决:

基于简单工厂模式的计算器


简单工厂模式结构图

简单工厂根据提供给它的参数(opera),经过必要的逻辑判断,决定哪个产品被创建,返回产品的公共父类。
产品基类Operation不一定非要是抽象类

---如果要改变Add的规则,只需要去更改OperationAdd()计算子类。(易维护好,只需更改类库中的计算子类!)
---如果要增加别的运算,如sin(), 需要增加运算子类OperationSin(), 还要更改OperationFactory类的switch逻辑判断分支。(可扩展性一般)
注意: 系统中增加业务规则类不是模式所能解决的,无论采用什么设计模式,OperationSin总是少不了的。(即增加了新系列产品)(TerryLee)
我们不能接受的是更改OperationFactory类,解决方法是用抽象工厂模式,用AddFactory生产OperationAdd,用SubFactory生产OperationSub ... 
---如果使用WinForm版的计算器,类库拿来即可使用. (复用性好)

“简单工厂模式实现计算器”代码在这里下载(拿了伍迷兄的改了改,呵呵)


 

抽象工厂模式引子
到底什么时候用抽象工厂模式,是值得思考的。就是说简单工厂模式的局限性在什么地方?
答:简单工厂模式不能应对“不同系列对象”的变化。
这是啥意思呢?
就是说,如果上面的计算器要卖给火星人用,那我们要针对火星数学法则,开发一套火星OperationAdd,火星OperationSub;
游戏场景设施工厂,我们要拓展游戏风格,要产生卡通风格的道路,建筑等等;简单工厂模式的FacilitiesFactory仅仅封装CreateRoad, CreateBuilding, createJungle就不能适用了。
这就是“不同系列对象”的变化。我们需要“隔离”这种变化。
另一个思考:
TerryLee的BonusTax例子,简单工厂模式也可以实现(只有一个工厂Factory;ChineseFactory,AmericanFactory其实只是用来做判断的):

对TerryLee例子的改动

客户端可以做到不用更改,只要改配置文件就能实现American和Chinese之间的迁移。
Bonus bonus = new Factory().CreateBonus();
double bonusValue  = bonus.Calculate();

可是这样做到底好不好呢?
其实这样做和TerryLee用抽象工厂写的是一样的效果。
因为这个例子,对象之间的“相互依赖”关系不是很明显(bonus和tax之间),所以才造成这样的结果。
如果增加France的,也是只需要增加FranceTax、FranceBonus,在Factory类增加相应的逻辑(改写为反射,逻辑也不用改了)。
反射: currentTax = (Tax)Assembly.Load("FactorySalary").CreateInstance("FactorySalary." + TaxName); 还要Bonus反射,从这点看还是AbstractFactory有利些。
而如果象下面的例子(肉食动物和食草动物之间存在依赖关系)用抽象工厂就十分必要了。
所以GoF给出的概念,每个字都不是白给的!(一系列相互依赖的对象

结论:
简单工厂模式能够实现一组对象的创建工作,但如果对象之间有依赖关系,则要用抽象工厂模式。

抽象工厂模式产品族示意图(来自吕震宇博文)

比较形象的表达了抽象工厂模式
 
抽象工厂模式Motivation

在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。(李建忠)
前者(只有一个系列)用简单工厂模式没有问题,后者因为需求的变化,"多系列",简单工厂模式会遇到麻烦。

抽象工厂模式意图

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂模式类图

客户程序只依赖抽象,AbstractFactoy, AbstractProductA, AbstractProductB, 实现了我们需要的隔离。
因为往往复杂的模块在Client模块,我们不希望为这部分做改动。例如:

抽象工厂模式 - 游戏设计sample

 

抽象工厂模式structrual code

Abstract Factory pattern -- Structural example

输出结果:
ProductB1 interacts with ProductA1
ProductB2 interacts with ProductA2

抽象工厂模式实例

Herbivore:草食动物    willebeest: 非洲羚羊  bison:美洲野牛
Carnivore:食肉动物    Lion: 非洲狮          jaguar: 美洲虎
尽管在不同大陆下动物物种是不一样的,但动物间的关系仍然保留了下来。(对象之间存在相互依赖的关系)
类图:

代码:

Abstract Factory pattern -- Real World example

Output
Lion eats Wildebeest
Jaguar eats Bison

从“OCR”来看抽象工厂模式

"开放-封闭"原则要求系统对扩展开放,对修改封闭。通过扩展达到增强其功能的目的。对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面
增加产品族(纵坐标):Abstract Factory很好的支持了"开放-封闭"原则。
增加新产品的等级结构(横坐标):需要修改所有的工厂角色,没有很好支持"开放-封闭"原则。
综合起来,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,而不能为新的产品等级结构的增加提供这样的方便。
所以用抽象工厂模式之前,一定要设计好系统用到的所用的产品等级结构,因为后来的改变将是剧烈的,系统对新的对象完全是陌生的!
需求分析的重要性!OO设计中要注意寻找变化点,为以后方便!
(系统有多少元素,building,bridge,road,...; Herbrivore, Carnivore...; Bonus, Tax,... 要先固定下来)

抽象工厂模式实现要点

l         抽象工厂将产品对象的创建延迟到它的具体工厂。

l         如果没有应对“多系列对象创建”的需求变化,则没有必要使用抽象工厂模式,这时候使用简单的静态工厂完全可以。

l         系列对象指的是这些对象之间有相互依赖、或作用的关系。

l         Abstract Factory主要在于应对“新系列”的需求变动,其缺点在于难以应对“新对象”的需求变动。

l         抽象工厂模式经常和工厂方法模式共同组合来应对“对象创建”的需求变化。

l         通常在运行时刻创建一个具体工厂类的实例,这一具体工厂的创建具有特定实现的产品对象,为创建不同的产品对象,客户应使用不同的具体工厂
例如ContnentFactory fac =
new AfricaFactory()。

l         把工厂作为单件,一个应用中一般每个产品系列只需一个具体工厂的实例,因此,工厂通常最好实现为一个单件模式。

l         创建产品,抽象工厂仅声明一个创建产品的接口,真正创建产品是由具体产品类创建的,最通常的一个办法是为每一个产品定义一个工厂方法,一个具体的工厂将为每个产品重定义该工厂方法以指定产品,虽然这样的实现很简单,但它确要求每个产品系列都要有一个新的具体工厂子类,即使这些产品系列的差别很小。

posted @ 2008-11-12 15:29  DylanWind  阅读(394)  评论(0编辑  收藏  举报