程序设计模式(二)创建型模式

来源:互联网 发布:用户行为分析python 编辑:程序博客网 时间:2024/06/11 19:51

一、一个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中的方法创建MazeMaze* 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的封装更隐蔽




































2 0