一、一个MAZE的构造过程
Maze的UML diagram如图所示
可以发现MapSite是斜体表示抽象类,有Room, Door, Wall三个子类继承,这三个就是用继承封装了MapSite的多向性。然而一个Maze,就是Room的聚合,所以Maze通过AddRoom方法来添加这些聚合这些Room. 最后通过MazeGame这个类,来构造这个Maze, 也就是帮助Maze这个对象执行它的AddRoom方法,帮Maze传递消息。所以我说, MazeGame就是Maze的创建类。重点看这个MazeGame创建类
class MazeGame {
public:
Maze* CreateMaze();//本节将要介绍的创建方法
Maze* CreateSimpleMaze();
Maze* CreateMaze(MazeFactory&);//后面讲的AbstractFactory模式
Maze* CreateMaze(MazeBuilder&);//后面讲的Builder模式
Maze* CreateComplexMaze (MazeBuilder& builder);
//构建各种不同迷宫的方法入口
};
看看CreateMaze()方法如何实现:
Maze* MazeGame::CreateMaze() {
Maze* aMaze = new Maze;
Room* r1 = new Room(1);
Room* r2 = new Room(2);
Door *theDoor = new Door(r1, r2);
aMaze->AddRoom(r1);
aMaze->AddRoom(r2);
r1->SetSide(North, new Wall);
r1->SetSide(East, theDoor);
r1->SetSide(South, new Wall);
r1->SetSide(West, new Wall);
r2->SetSide(North, new Wall);
r2->SetSide(East, new Wall);
r2->SetSide(South, new Wall);
r2->SetSide(West, theDoor);
return aMaze;
}
可以看出,这个Maze被create出来的结构是单一的,就只可能是两个房间,如何自由定义房间空间个数和四壁的内容而不重写代码,这个问题后面是不能解决的。同时,the room, the wall, the door的性质,在SetSide里面已经定义死了,如果下面我们需要给这个Maze定义两种风格,第一种风格是room加上魔法room,door加上用魔法才能打开的魔法Maze, 第二种风格room里面可能有炸弹,走到炸弹里面就死了的炸弹Maze, 用非继承的方法,那么我们呢需要重新写两个Room这个类,在EnchantedMaze中要扩展一个EnchantedRoom类,在BombedMaze中要扩展RoomWithABoom类,如下图所示。
一般的思维,是在CreateMaze的时候,输入的对象还是那个Maze, 但需要给Maze添加一个风格属性,同时在CreateMaze()开头就要添加一行语句判断创建的是哪种风格的Maze, 然后分别对不同的Maze.style, 需要改Room* r1 = new Room(1) 为 Room* r1 = new EnchantedRoom(1) 和 Room* r1 = new RoomWithABomb(1) ,这样一下子就增加了很多的代码量,因为case变多了。所以第一个想法就是调整CreateMaze的输入,这样引出了第一种模式AbstractFactory.
二、AbstractFactory
在我们定义一个基础类AbstractFactory, 然后再派生出ConcreteFactory, 这每一个ConcreteFactory的类里面都定义了各自创属于自己风格的Room和Wall的方法。
带入到Maze里面的代码如下
class MazeFactory {
public:
MazeFactory();
virtual Maze* MakeMaze() const;
virtual Wall* MakeWall() const;
virtual Room* MakeRoom(int n) const;
virtual Door* MakeDoor(Room* r1, Room* r2) const;
}; //这个是基础的AbstractFactory,以后不同风格的ConcreteFactory都是继承此。
//AbstractFactory提供创建Maze类对象的方法,并且提供创建Wall,Room,Door的方法,然后让CreateMaze()来调用这个Factory中的方法创建Maze
Maze* MazeFactory::MakeMaze() const
{ return new Maze; }
Wall* MazeFactory::MakeWall() const
{ return new Wall; }
Room* MazeFactory::MakeRoom(int n) const
{ return new Room(n); }
Door* MazeFactory::MakeDoor(Room* r1, Room* r2) const
{ return new Door(r1, r2); }
下面是实现了BomdedMazeFactory
class BombedMazeFactory : public MazeFactory {
public:
BombedMazeFactory();
virtual Wall* MakeWall() const;
virtual Room* MakeRoom(int n) const;
};
Wall* BombedMazeFactory::MakeWall () const {
return new BombedWall;
}
Room* BombedMazeFactory::MakeRoom (int n) const {
return new RoomWithABomb(n);
}注意这里虚函数的重写,没有把Abstract中的所有虚函数都重写,就重写了需要改变的Room 和 Wall
同理是EnchantedMazeFactory的实现
class EnchantedMazeFactory : public MazeFactory {
public:
EnchantedMazeFactory();
virtual Room* MakeRoom(int n) const;
virtual Door* MakeDoor(Room* r1, Room* r2) const;
protected:
Spell* CastSpell() const;
};
Room* EnchantedMazeFactory::MakeRoom(int n) const
{ return new EnchantedRoom(n, CastSpell()); }
Door* EnchantedMazeFactory::MakeDoor(Room* r1, Room* r2) const
{ return new DoorNeedingSpell(r1, r2); }
把上面这个放到CreateMaze()中就是如下所示,注意我们完全没有必要粉case讨论,直接输入AbstractFactory基类类型就可以,
Maze* MazeGame::CreateMaze (MazeFactory& factory) {
Maze* aMaze = factory.MakeMaze();
Room* r1 = factory.MakeRoom(1);
Room* r2 = factory.MakeRoom(2);
Door* aDoor = factory.MakeDoor(r1, r2);
aMaze->AddRoom(r1);
aMaze->AddRoom(r2);
r1->SetSide(North, factory.MakeWall());
r1->SetSide(East, aDoor);
r1->SetSide(South, factory.MakeWall());
r1->SetSide(West, factory.MakeWall());
r2->SetSide(North, factory.MakeWall());
r2->SetSide(East, factory.MakeWall());
r2->SetSide(South, factory.MakeWall());
r2->SetSide(West, aDoor);
return aMaze;
}
然后想要创建哪种风格的Maze,就输入相应的ConcreteFactory, 输入进去了就可以形成多态,CreateMaze()里面的代码就一行不用改了。创建代码如下
BombedMazeFactory BFactory;
EnchantedMazeFactory EFactory;
Maze* pBMaze = MazeGame::CreateMaze(BFactory);
Maze* pEMaze = MazeGame::CreateMaze(EFactory);
所以,现在就知道了如何利用AbstractFactory来通过AbstractFactory派生到ConcreteFactroy的过程,就在这些类里面封装了我们所需要的变化,因此在CreateMaze里面就可以用单一的代码动态的展现我们的多种风格Maze的生成,这就是模式的精华。
这个里面还要注意的是,MazeFactory这个类里面不含纯虚函数,可以直接生成对象,所以他不是抽象类。
但是注意,我们这里一旦选定了factory的种类,CreateMaze的任务比如AddRoom(), SetSide()还是在CreateMaze()方法中显示完成的,就与接下来的Builder模式不一样了,因为Builder模式把上面Room的具体实现形式都封装到了Builder对象中了。
2.Builder模式
迷宫Builder的基类
class MazeBuilder {
public:
virtual void BuildMaze() { }
virtual void BuildRoom(int room) { }
virtual void BuildDoor(int roomFrom, int roomTo) { }
virtual Maze* GetMaze() { return 0; }
protected:
MazeBuilder();
};
MazeGame中CreateMaze()方法重新定义如下
Maze* MazeGame::CreateMaze (MazeBuilder& builder)
{
builder.BuildMaze();
builder.BuildRoom(1);
builder.BuildRoom(2);
builder.BuildDoor(1, 2);
return builder.GetMaze();
}
对比与AbstractFactory模式,可以发现CreateMaze中所有的对Maze制造过程都只依赖builder对象,不需要对Maze对象直接操作,而是把对Maze对象的操作放到了Builder里面的方法中完成。当我们具体定义一个Builder的时候,通常要把基类的Builder继承,如派生一个StandardMazeBuilder
class StandardMazeBuilder : public MazeBuilder {
public:
StandardMazeBuilder();
virtual void BuildMaze();
virtual void BuildRoom(int);
virtual void BuildDoor(int, int);
virtual Maze* GetMaze();
private:
Direction CommonWall(Room*, Room*);
Maze* _currentMaze;
};
下面看建造Maze的方法,本来是要调用Maze对象来完成,但是现在是被封装到了Builder对象中了
StandardMazeBuilder::StandardMazeBuilder () {
_currentMaze = 0;
}
void StandardMazeBuilder::BuildMaze () {
_currentMaze = new Maze;
}
Maze *StandardMazeBuilder::GetMaze () {
Maze* maze = _currentMaze;
return maze;
}
void StandardMazeBuilder::BuildRoom (int n) {
if (!_currentMaze->RoomNo(n)) {
Room* room = new Room(n);
_currentMaze->AddRoom(room);
room->SetSide(North, new Wall);
room->SetSide(South, new Wall);
room->SetSide(East, new Wall);
room->SetSide(West, new Wall);
}
}
void StandardMazeBuilder::BuildDoor (int n1, int n2) {
Room* r1 = _currentMaze->RoomNo(n1);
Room* r2 = _currentMaze->RoomNo(n2);
Door* d = new Door(r1, r2);
r1->SetSide(CommonWall(r1,r2), d);
r2->SetSide(CommonWall(r2,r1), d);
}
这样一来,在CreateMaze()方法中代码就更简单了,这样完全将Maze的构建和Maze对象可以隔离开来,单纯用builder对象就可以完成了。同理,对于不同风格的Maze的创建,需要继承不同种类的Builder. 而且,builder的基类也不是抽象类,因为里面如果有纯虚函数一定要被实现,而实际中builder的派生出来的conceate builder中差别比较大,不一定每个都会用到基类中的纯虚函数, 所以就没必要采用抽象类来作为基类。
最终,采用下面代码来实现一个maze对象
void Test1() {
Maze* maze;
MazeGame game;
StandardMazeBuilder builder;
game.CreateMaze(builder);
maze = builder.GetMaze();
}
最后总结一下AbstractFactroy和Builder的联系和区别:
1. 两个基类都不是抽象类
2. Builder比Abstract的封装更隐蔽