问题聚焦:
工厂方法与抽象工厂的区别
别名:虚构造器(Virtual Constructor)
意图:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
别名:
虚构造器,Virutal Constructor
动机:依然用一个实例来说明
Demo
考虑一个应用框架,它可以向用户显示多种文档。
两个抽象:Application(管理和创建Document),Document
例如绘图应用:DrawingApplication,DrawingDocument
特点:因为被实例化的特定Document子类是与特定应用相关的,所以Application类不可能预测到哪个Document子类将被实例化。
解决方案:封装哪一个Document子类将被创建的信息,并将这些信息从该框架中分离出来。
架构:
Application的子类重定义Application的抽象操作CreateDocument以返回适当的Document子类对象,一旦一个Application子类实例化以后,它就可以实例化与一个用相关的文档,而无需知道这些文档的类。
适用性
- 下列情况考虑使用Factory Method模式
- 当一个类不知道它所必须创建的对象的类的时候
- 当一个类希望由它的子类来指定它所创建的对象的时候
- 当类创建对象的职责委托给多个帮助子类的某一个,并且你希望哪一个帮助子类是代理者这一信息局部化的时候
结构
参与者
- Product(Document): 定义工厂方法所创建的对象的接口
- ConcreteProduct(MyDocutment): 实现Product接口
- Creator(Application):声明工厂方法,返回一个Product类型的对象,Creator也可以定义一个工厂方法的缺省实现,它返回一个缺省的ConcreteProduct对象;可以调用工厂方法以创建一个Product对象
- ConcreteCreator(MyApplication):重定义工厂方法以返回一个ConcreteProduct实例
协作
Creator依赖于它的子类来定义工厂方法,所以它返回一个适当的ConcreteProduct实例
效果
不再将与特定应用相关的类绑定到你的代码中,代码仅处理Product接口
为子类提供挂钩:用工厂方法在一个类的内部创建对象通常比直接创建对象更灵活
连接平行的类层次:工厂方法往往并不只是被Creator调用,客户可以找到一些有用的工厂方法。
解析:平行类层次:当一个类将它的一些职责委托给一个独立的类时,就产生了平行类层次。
考虑一个例子功能:对一个图形,用鼠标进行伸展、移动或者旋转难点:需要存储和更新在给定时刻操作状态的信息;不同的图形有不同的行为设计:Figure(图形类)、Manipulator(操作类,实现交互,保存操作相关状态)![]()
从Client的角度看,操作类层次和图形类层次是平行的。Figure类提供一个CreateManipulator工厂方法,它使得客户可以创建一个与Figure相对应的Manipulator。
实现
1 当应用工厂方法模式时要考虑下面的问题:
两种不同的情况
Creator类是一个抽象类,并且不提供它所声明的工厂方法(必须实例化子类才能使用)Creator类是一个具体类,而且为工厂方法提供一个缺省的实现()
2 参数化工厂方法
工厂方法支持传递参数来创建多种产品Demo一个参数化的工厂方法具有如下的一般形式class Creator { public: virtual Product* Creator(ProductId); }; Product* Creator::Create (ProductId id) { if (id == MINE) return new MyProduct; if (id == YOURS) return new YourProduct; return 0; } // 子类MyCreator继承Creator product* MyCreator::Create (ProductId id) { if (id == YOURS) return new MyProduct; if (id == MINE) return new YourProduct; if (id == THEIRS) return new TheirProduct; return Creator::Create(id); // 如果都不匹配,返回父类的Create }
3 特定语言的变化和问题
4 使用模板以避免创建子类在C++中记住一点,不要在Creator或者其子类的构造器中调用工厂方法。访问者希望或得产品时,访问者首先检查一下该产品是否存在,如果产品不存在,访问者就创建它。这种技术称为lazy initialization。
5 命名约定工厂方法的一个潜在问题就是,可能仅为了创建适当的Product对象而迫使你创建Creator子类。(当然,可以用参数化工厂方法来解决这个问题)另一个解决办法:提供Creator一个模板子类,使用Product类作为模板参数。class Creator { public: virtual Product* CreateProduct() = 0; }; template <class TheProduct> class StandardCreator: public Creator { public: virtual Product* CreateProduct() ; } template <class TheProduct> Product* StandardCreator<TheProduct>::CreateProduct() { return new TheProduct; } // 使用这个模板 class MyProduct : public Product { public: MyProduct(); // ...... }; StandardCreator<MyProduct> myCreator;
命名以清楚地说明你正在使用工厂方法
代码示例
使用工厂方法建造一个迷宫,使子类可以选择这些构件。每一个工厂方法返回一个给定类型的迷宫构件。
class MazeGame {
public:
Maze* CreateMaze();
virtual Maze* MakeMasze() const
{ return new Maze;}
virtual Room* MakeRoom(int n) const
{ return new Room(n) };
virtual Wall* MakeWall() const
{ return new Wall;}
virtual Door* MakeDoor(Room* r1, Room* r2) const
{ return new Door(r1, r2); }
}
不同的游戏可以创建MazeGame的子类,以特别指明一些迷宫的部件。MazeGame子类可以重定义一些或所有的工厂方法以指定产品中的变化。
如:
class BombedMazeGame: public MazeGame {
public:
BombedMazeGame();
virtual Wall* MakeWall() const
{ return new BombedWall; }
virtual Room* MakeRoom(int n) const
{ return new RoomWithABomb(n); }
};
相关模式
Abstract Factory经常用工厂方法实现;
在Template Method中被调用
Prototypes不需要创建Creator的子类,但是它们通常要求一个针对Product类的Initialize操作。Creator使用Initialize来初始化对象,而Factory Method不需要这样的操作。
参考资料:
《设计模式》