在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时由于需求的变化,往往存在着更多系列对象的创建工作。如何应对这种变化?如何绕过常规的对象的创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?这就是我们要说的抽象工厂模式。
简单工厂模式引子(从重构来理解)
思考:New存在问题么?
Sting str=new String() 我们平常都是这样用,怎么会有问题呢?
那我们再来看:
类库---
class Road
{
//
}
客户程序---
Road road = new Road();
有问题么?应该没有吧...其实...
new确实存在问题:实现依赖,不能应对“具体实例化类型”的变化。
就是说Road()发生变化,比方说参数发生变化, 不仅要改Road类,客户程序Road road = new Road() 也需要更改
String类几乎不发生什么改变,是稳定的,就不存在这个问题
简单工厂模式又称为静态工厂模式:
封装变化点,哪里变化,封装哪里 没有变化就不用封装
---变化点在“对象创建” 所以封装对象创建, wrapper Road类
简单工厂模式 structural code
类库---
class RoadFactory
{
public static Road CreateRoad()
{
return new Road();
}
}
class Road
{
//
}
客户程序---
Road road = RoadFactory.CreteRoad();
这种封装其实是把实例化提出到另一处地方,相当于过程式程序设计中的函数的概念。
面对新的需求变化,例如Road构造方法发生变化,简单工厂模式将变化隔离在类库中。
思考:如果我们增加一种Bridge呢?在类库中增加Bridge类,CreateBridge方法。
简单工厂模式实例
简单工厂模式对程序的复用性,易维护,可扩展带来什么好处呢?看例子(来自cj723"大话设计模式")
假设"TO"公司做了一个Operation的类,封装了常用的计算方法。
//Operation.cs
---未使用工厂模式,我们直接拿来使用:
operation oper = new operationSub();
oper.NumberA = 56;
oper.NumberB = 8;
double c = oper.GetResult();
同样存在“实现依赖”的问题;
---使用简单工厂模式解决:
基于简单工厂模式的计算器
增加OperationFactory对Operation类wrapper
//OperationFactory.cs
public class OperationFactory
{
public static Operation CreateOperator(string opera)
{
Operation operation = null;
switch (opera)
{
case "+":
{
operation = new OperationAdd();
break;
}
//.
default:
break;
}
return operation;
}
}
客户程序
Console.Write("请输入数字A:");
string strNumberA = Console.ReadLine();
Console.Write("请选择运算符号(+、-、*、/):");
string strOperate = Console.ReadLine();
Console.Write("请输入数字B:");
string strNumberB = Console.ReadLine();
string strResult = "";
Operation oper = OperationFactory.CreateOperator(strOperate);
oper.NumberA = Convert.ToDouble(strNumberA);
oper.NumberB = Convert.ToDouble(strNumberB);
strResult = oper.GetResult().ToString();
简单工厂模式结构图
简单工厂根据提供给它的参数(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例子的改动
public class Factory
{
public Tax CreateTax()
{
Tax currentTax = null;
if (Constant.STR_FACTORYNAME == "ChineseFactory")
{
currentTax = new ChineseTax();
}
else if (Constant.STR_FACTORYNAME == "AmericanFactory")
{
currentTax = new AmericanTax();
}
return currentTax;
}
public Bonus CreateBonus()
{
Bonus currentBonus = null;
if (Constant.STR_FACTORYNAME == "ChineseFactory")
{
currentBonus = new ChineseBonus();
}
else if (Constant.STR_FACTORYNAME == "AmericanFactory")
{
currentBonus = new AmericanBonus();
}
return currentBonus;
}
}
客户端可以做到不用更改,只要改配置文件就能实现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
// 客户端只依赖 抽象工厂FacilitiesFactory 和 抽象产品Road Building
public class GameManager
{
FacilitiesFactory facilitiesFactory;
Road road;
Building building;
Tunnel tunnel;
Jungle jungle;
public GameManager( FacilitiesFactory facilitiesFactory )
{
this.facilitiesFactory = facilitiesFactory;
}
public void BuildGameFacilities()
{
road = facilitiesFactory.CreateRoad();
building = facilitiesFactory.CreateBuilding();
tunnel = facilitiesFactory.CreateTunnel();
jungle = facilitiesFactory.CreateJungle();
}
public void Run()
{
//运行逻辑部分 十分复杂 不希望改动
road.AAA();
buliding.BBB(road);
tunnel.CCC(jungle);
}
}
主程序:
GameManager g = new GameManager(new ModernFacilitiesFactory());
g.BuildGameFacilities();
g.Run();
抽象工厂模式structrual code
Abstract Factory pattern -- Structural example
using System;
namespace GangOfFour.Abstract.Structural
{
// MainApp test application
class MainApp
{
public static void Main()
{
// Abstract factory #1
AbstractFactory factory1 = new ConcreteFactory1();
Client c1 = new Client(factory1);
c1.Run();
// Abstract factory #2
AbstractFactory factory2 = new ConcreteFactory2();
Client c2 = new Client(factory2);
c2.Run();
// Wait for user input
Console.Read();
}
}
// "AbstractFactory"
abstract class AbstractFactory
{
public abstract AbstractProductA CreateProductA();
public abstract AbstractProductB CreateProductB();
}
// "ConcreteFactory1"
class ConcreteFactory1 : AbstractFactory
{
public override AbstractProductA CreateProductA()
{
return new ProductA1();
}
public override AbstractProductB CreateProductB()
{
return new ProductB1();
}
}
// "ConcreteFactory2"
class ConcreteFactory2 : AbstractFactory
{
public override AbstractProductA CreateProductA()
{
return new ProductA2();
}
public override AbstractProductB CreateProductB()
{
return new ProductB2();
}
}
// "AbstractProductA"
abstract class AbstractProductA
{
}
// "AbstractProductB"
abstract class AbstractProductB
{
public abstract void Interact(AbstractProductA a);
}
// "ProductA1"
class ProductA1 : AbstractProductA
{
}
// "ProductB1"
class ProductB1 : AbstractProductB
{
public override void Interact(AbstractProductA a)
{
Console.WriteLine(this.GetType().Name +
" interacts with " + a.GetType().Name);
}
}
// "ProductA2"
class ProductA2 : AbstractProductA
{
}
// "ProductB2"
class ProductB2 : AbstractProductB
{
public override void Interact(AbstractProductA a)
{
Console.WriteLine(this.GetType().Name +
" interacts with " + a.GetType().Name);
}
}
// "Client" - the interaction environment of the products
class Client
{
private AbstractProductA AbstractProductA;
private AbstractProductB AbstractProductB;
// Constructor
public Client(AbstractFactory factory)
{
AbstractProductB = factory.CreateProductB();
AbstractProductA = factory.CreateProductA();
}
public void Run()
{
AbstractProductB.Interact(AbstractProductA);
}
}
}
输出结果:
ProductB1 interacts with ProductA1
ProductB2 interacts with ProductA2
抽象工厂模式实例
Herbivore:草食动物 willebeest: 非洲羚羊 bison:美洲野牛
Carnivore:食肉动物 Lion: 非洲狮 jaguar: 美洲虎
尽管在不同大陆下动物物种是不一样的,但动物间的关系仍然保留了下来。(对象之间存在相互依赖的关系)
类图:
代码:
Abstract Factory pattern -- Real World example
using System;
// "AbstractFactory"
abstract class ContinentFactory
{
// Methods
abstract public Herbivore CreateHerbivore();
abstract public Carnivore CreateCarnivore();
}
// "ConcreteFactory1"
class AfricaFactory : ContinentFactory
{
// Methods
override public Herbivore CreateHerbivore()
{ return new Wildebeest(); }
override public Carnivore CreateCarnivore()
{ return new Lion(); }
}
// "ConcreteFactory2"
class AmericaFactory : ContinentFactory
{
// Methods
override public Herbivore CreateHerbivore()
{ return new Bison(); }
override public Carnivore CreateCarnivore()
{ return new Jaguar(); }
}
// "AbstractProductA"
abstract class Herbivore
{
}
// "AbstractProductB"
abstract class Carnivore
{
// Methods
abstract public void Eat( Herbivore h );
}
// "ProductA1"
class Wildebeest : Herbivore
{
}
// "ProductB1"
class Lion : Carnivore
{
// Methods
override public void Eat( Herbivore h )
{
// eat wildebeest
Console.WriteLine( this + " eats " + h );
}
}
// "ProductA2"
class Bison : Herbivore
{
}
// "ProductB2"
class Jaguar : Carnivore
{
// Methods
override public void Eat( Herbivore h )
{
// Eat bison
Console.WriteLine( this + " eats " + h );
}
}
// "Client"
class AnimalWorld
{
// Fields
private Herbivore herbivore;
private Carnivore carnivore;
// Constructors
public AnimalWorld( ContinentFactory factory )
{
carnivore = factory.CreateCarnivore();
herbivore = factory.CreateHerbivore();
}
// Methods
public void RunFoodChain()
{ carnivore.Eat(herbivore); }
}
/**//// <summary>
/// GameApp test class
/// </summary>
class GameApp
{
public static void Main( string[] args )
{
// Create and run the Africa animal world
ContinentFactory africa = new AfricaFactory();
AnimalWorld world = new AnimalWorld( africa );
world.RunFoodChain();
// Create and run the America animal world
ContinentFactory america = new AmericaFactory();
world = new AnimalWorld( america );
world.RunFoodChain();
}
}
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 创建产品,抽象工厂仅声明一个创建产品的接口,真正创建产品是由具体产品类创建的,最通常的一个办法是为每一个产品定义一个工厂方法,一个具体的工厂将为每个产品重定义该工厂方法以指定产品,虽然这样的实现很简单,但它确要求每个产品系列都要有一个新的具体工厂子类,即使这些产品系列的差别很小。