意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到了其子类。
适用性
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类来指定它所创建的对象的时候。
- 当类创建对象的职责委托给多个帮助子类中的某一个,并且希望这个帮助子类的信息局部化,即需要降低耦合。
结构
- Product
定义工厂方法所创建对象的接口。 - ConcreteProduct
实现Product接口。 - Creator
声明工厂方法,该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的缺省实现,它返回一个缺省的ConcreteProduct对象。 - ConcreteCreator
重定义工厂方法以返回一个ConcreteProduct实例。
协作
Creator依赖于它的子类来定义工厂方法,所以需要它返回一个适当的ConcreteProduct实例。
效果
工厂方法不再将特定应用有关的类绑定到你的代码中,代码仅处理Product接口,因此它可以与用户定义的任何ConcreteProduct类一起使用,提高了可扩展性,降低了耦合度。
工厂方法一个潜在缺点是客户为了创建一个特定的ConcreteProduct对象,就不得不创建Creator的子类。
实现
需要考虑以下几个方面:
1)有两种情况,一是Creator类是抽象类不提供它所声明的工厂方法的实现;二是Creator是一个具体的类为工厂方法提供了一个缺省的实现。
第一种情况必须子类来定义实现,因为没有缺省实现,它避免了不得不实例化不可预见类的问题。第二种情况,使子类的设计者能够在必要时才需要改变父类所实例化对象的类。
2)参数化工厂方法,工厂方法也可以使用一个标识符作为参数来决定要创建的对象种类。
class Creator {
public:
virtual Product* Creator(ProductId);
};
Product* Creator::Create (ProductId id) {
if (id == MINE) return new MyProduct;
if (id == YOUS) return new YourProduct;
// repeat for remaining products...
return 0;
}
参数化得工厂方法使你可以简单而有选择性的扩展或改变一个Creator生产的产品。可以为新产品引入新的标识符,或可以将已有的标识符与不同的产品关联。
3)不要在构造器中调用虚函数。C++中工厂方法都是虚函数并且常常是纯虚函数,一定要注意在Creator的构造器中不要调用工厂方法,因为此时ConcreteCreator中的该工厂方法还不可用。
4)使用模板以避免创建子类。正如上面提及的,工厂方法一个潜在的问题是它们仅为了创建适当的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>::CreatProduct() {
return new TheProduct;
}
//使用这个模板,客户仅需提供产品类,而不需要创建Creator子类
class MyProduct : public Product {
public:
MyProduct();
// ...
};
StandardCreator<MyProduct> myCreator;
5) 命名约定。使用命名约定是一个好习惯,它可以清楚的说明你正在使用工厂方法。例如可以总是定义工厂方法为:Class* DoMakeClass()
,此次Class为Product类。
示例
// MazeGame.hpp
// 有默认实现的工厂方法基类
#pragma once
#include "Maze.hpp"
class MazeGame {
public:
Maze * CreateMaze();
// factory methods:
virtual Maze* MakeMaze() 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);
}
};
Maze* MazeGame::CreateMaze()
{
Maze* pMaze = MakeMaze();
Room* r1 = MakeRoom(1);
Room* r2 = MakeRoom(2);
Door* pDoor = MakeDoor(r1, r2);
pMaze->AddRoom(r1);
pMaze->AddRoom(r2);
r1->SetSide(North, MakeWall());
r1->SetSide(East, pDoor);
r1->SetSide(South, MakeWall());
r1->SetSide(West, MakeWall());
r2->SetSide(North, MakeWall());
r2->SetSide(East, MakeWall());
r2->SetSide(South, MakeWall());
r2->SetSide(West, pDoor);
return pMaze;
}
不同的游戏可以创建MazeGame的子类并特化一些迷宫组件,即MazeGame子类可以重定义一些或所有的工厂方法以指定产品中的变化。例如,一个BombedMazeGame可以重定义产品Room和Wall以返回爆炸后的变体:
class BombedMazeGame : public MazeGame {
public:
BombedMazeGame() {}
virtual Wall* MakeWall() const
{
return new BombedWall;
}
virtual Room* MakeRoom(int n) const
{
return new RoomWithABomb(n);
}
};
一个EnchantedMazeGame变体可以这样定义:
class EnchantedMazeGame : public MazeGame {
public:
EnchantedMazeGame() {}
virtual Room* MakeRoom(int n) const
{
return new EnchantedRoom(n, CastSpell());
}
virtual Door* MakeDoor(Room* r1, Room* r2) const
{
return new DoorNeedingSpell(r1, r2);
}
protected:
Spell * CastSpell() const
{
std::cout << "Cast Spell" << std::endl;
}
};
相关模式
Abstract Factory经常使用工厂方法来实现。
工厂方法通常在Template Methods中被调用。
Prototypes不需要创建Creator的子类,但它们通常要求一个针对Product类的Initialize操作。即Creator使用Initialize来初始化对象,而Factory Method不需要。