飞机工厂一个厂房生产3种飞机(看001.Simple Factory模式.doc), 给人的感觉就是不专业, 而且浪费成本, 在增加一种机种时需要对工厂进行修改(缺点).
因此飞机工厂设计了一个方案: 就是飞机工厂作为总工厂, 总工厂下面设各种机种的工厂, 总工厂只负责定制飞机申请流程, 下属飞机工厂实现总工厂定制的流程并生产指定飞机, 用户申请飞机就到指定的飞机工厂申请即可.
例如: 直升飞机工厂只生产直升飞机, 游泳飞机厂只生产游泳飞机, ...
如果以后增加一种太空飞机的话, 则直接建一个"太空飞机厂"即可.
用户申请飞机, 就先到指定的飞机工厂, 申请要飞机就可以了(但要按照总工厂定制的流程做).
抽象工厂(Creator)角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建对象的工厂类必须实现这个接口。 (飞机总工厂)
具体工厂(Concrete Creator)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。(直升飞机厂, 潜水飞机厂...)
抽象产品(Product)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。(飞机)。
具体产品(Concrete Product)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应.(直升飞机, 潜水飞机...)
3种飞机的共同特性就是可以飞Fly, Fly的结果就是飞到指定目的地, 但Fly的体现不同.
直升飞机 : 普通速度飞行,垂直升降(特殊功能).
游泳飞机: 水上飞行, 潜水(特殊功能).
超音速飞机: 快速飞行, 超音速飞行(特殊功能).
1. 用户A(直升飞机师)说我要从越秀山飞到白云上, 于是用户A就按照飞机总工厂定制的流程到直升飞机工厂 我要一架直升飞机, 工厂给了用户一架直升飞机, 用户使用直升飞机的飞行功能(普通速度飞行)飞行到目的地, 并且使用了直升飞机的特殊功能(垂直升降).
...
2. 用户E(太空飞机师)说我要去火星, 但是用户E根本找不着生产太空飞机的地方.
3. 飞机工厂心里想 Shit, 生产不了太空飞机, 有钱都赚不了. 于是飞机总工厂就建立了太空飞机工厂研发了太空飞机. 虽然增加了太空飞机工厂, 但是各个飞机工厂只负责自己的飞机的生产, 所以增加了太空飞机工厂, 对之前的飞机工厂的运作没有影响, 对之前的用户的申请流程也没有影响. 用户E按照飞机总工厂定制的流程到太空飞机工厂要飞机即可.
Factory Method模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中.核心工厂类不再负责产品的创建.这样核心类成为一个抽象工厂角色.仅负责具体工厂子类必须实现的接口.这样进一步抽象化的好处是使得工 Factory Method模式可以使系统在不修改具体工厂角色的情况下引进新的产品.
在飞机总工厂例子中就是这样, 总工厂只定制申请流程, 流程的实现在具体飞机工厂中实现, 用户按流程申请自己会使用的飞机, 当增加太空飞机工厂时, 总工厂的流程没有变, 用户申请的飞机工厂也没有变, 前面的用户就没有变的理由了.
// 飞机抽象类
class CPlane
{
public:
CPlane(){};
virtual ~CPlane(){};
virtual void Fly() = 0;
};
// 游泳飞机
class CSwimPlane : public CPlane
{
public:
CSwimPlane();
virtual ~CSwimPlane();
virtual void Fly();
void Dive();
};
// 直升飞机
class CHolicopter : public CPlane
{
public:
CHolicopter();
virtual ~CHolicopter();
virtual void Fly();
void VerticalUpDown();
};
// 超音速飞机
class CSoundPlane : public CPlane
{
public:
CSoundPlane();
virtual ~CSoundPlane();
virtual void Fly();
void SoundFly();
};
// 各个飞机工厂
// 抽象飞机工厂
class CHQPlaneFactory
{
public:
CHQPlaneFactory();
virtual ~CHQPlaneFactory();
virtual CPlane* CreatePlane() = 0; // 定义了飞机工厂的公共接口
};
// 直升飞机工厂
class CHolicopterFactory : public CHQPlaneFactory
{
public:
CHolicopterFactory();
virtual ~CHolicopterFactory();
virtual CPlane* CreatePlane();
};
CPlane* CHolicopterFactory::CreatePlane() // 实现接口
{
return new CHolicopter;
}
// 超音速飞机工厂
class CSoundPlaneFactory : public CHQPlaneFactory
{
public:
CSoundPlaneFactory();
virtual ~CSoundPlaneFactory();
virtual CPlane* CreatePlane();
};
CPlane* CSoundPlaneFactory::CreatePlane() // 实现接口
{
return new CSoundPlane;
}
…
// 调用
// 使用步骤就是先产生对应飞机的工厂, 再使用工厂的方法生产飞机, 再使用飞机的功能.
// 各个用户的调用
// 1
CHQPlaneFactory * pSoundPlaneFactory = new CSoundPlaneFactory;
CPlane* pSoundPlane = pSoundPlaneFactory->CreatePlane();
pSoundPlane->Fly();
// 如果你了解该飞机的功能, 你可以
static_cast<CSoundPlane*>(pSoundPlane)->SoundFly();
// 2
CHQPlaneFactory * pSwimPlaneFactory = new CSwimPlaneFactory;
CPlane* pSwimPlane = pSwimPlaneFactory->CreatePlane();
pSoundPlane->Fly();
// 如果你了解该飞机的功能, 你可以
static_cast<CSwimPlane*>(pSwimPlane)->Dive();
// 3
CHQPlaneFactory * pHolicopterFactory = new CHolicopterFactory;
CPlane* pHolicopter = pHolicopterFactory->CreatePlane();
pHolicopter->Fly();
// 如果你了解该飞机的功能, 你可以
static_cast<CHolicopter*>(pHolicopter)->VerticalUpDown();
// 4 以后增加的太空飞机对于1,2,3的调用是没有影响的, 而且也不需要2 3重新编译
// 5 可以看到, 1,2,3的调用步骤是一样的, 也就是调用流程不变, 我们对于那些不变的共性的部分, 使用抽象.
代码在 http://download.csdn.net/source/2816683
002FactoryMethod.rar
1. Factory Method模式涉及的角色
1) 抽象工厂(Creator)角色:担任这个角色的是工厂方法模式的核心, 它指定创建对象的抽象方法.
2) 具体工厂(Concrete Creator)角色:实现创建具体的对象.
3) 抽象产品角色:不多说
4) 具体产品角色:不多说.
2. Factory Method模式与其他模式的比较
工厂方法模式与简单工厂模式再结构上的不同不是很明显。工厂方法类的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上。
工厂方法模式之所以有一个别名叫多态性工厂模式是因为具体工厂类都有共同的接口,或者有共同的抽象父类。
当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了"开放-封闭"原则。而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。
1. 在Simple Factory模式中, 我的热敏打印机打印小票模块通过在DLL中实现, 并且通过配置文件来传入打印机类型, 这样在如果有新打印机添加时, 我只需要修改工厂类的方法, 重新编译DLL, 并增加配置文件中打印机的类型即可. 使用DLL和配置, 实现了我的调用代码不需要修改和编译. (打印机的共同属性被抽象了, 但创建不同类型打印机部分没有被抽象了)(调用代码就是读取配置打印机的类型, 把类型传入DLL的类工厂创建方法中, 创建方法返回指定类型的打印机对象).
2. 使用Factory Method模式的话, 我不需要修改工厂类的方法, 而是增加一个打印机类型的工厂, 和一个打印机类. (打印机的共同属性被抽象了, 创建不同类型打印机部分也被抽象了). 通过Factory Method模式来实现了我的调用哪个代码不需要修改和编译.
3. 对比1和2, 在添加打印机类型后, 调用代码都可以不需要修改和编译, 但是从面向对象角度说, 2明显比1更"面向对象", 原因是2更抽象了, 而1中如果不使用DLL作为载体, 增加了打印机类型, 调用代码必须重新编译才可以正确运行. 而2 对于以前的代码是兼容的, 符合"开-闭"原则, 对于前面的调用者不需要修改调用代码, 也支持后面新增打印机类型.
4. 体会一下, 不太会表达.
http://liulve-rover-163-com.javaeye.com/blog/503033
http://www.ideagrace.com/html/doc/2008/01/21/08686.html