【需求分析】
假设我们需要写一个迷宫游戏,游戏支持用户自由选择游戏的场景,比如可以选择普通的迷宫,或者是有魔法的迷宫等。但所有的迷宫都有一些共同的元素,包括墙壁、门、房间。
只考虑创造一个普通的迷宫,代码比较简单,如下所示:
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, factory.MakeWall()); r1->SetSide(East, theDoor); r1->SetSide(South, factory.MakeWall()); r1->SetSide(West, factory.MakeWall()); r2->SetSide(North, factory.MakeWall()); r2->SetSide(West, theDoor); r2->SetSide(South, factory.MakeWall()); r2->SetSide(East, factory.MakeWall()); return aMaze;
为了让迷宫支持更多的差异化,我们把迷宫的属性进行细分,可以发现不同的迷宫间有共同的元素,不同的只是元素的属性,或者说元素的实现方式。因此,我们希望这些共同的元素能够被工厂动态的生产出来,然后加入到迷宫当中,如下所示:
aMaze = factory.MakeMaze(); r1 = factory.MakeRoom(1); r2 = factory.MakeRoom(2); theDoor = factory.MakeDoor(r1, r2); aMaze->AddRoom(r1); aMaze->AddRoom(r2); r1->SetSide(North, factory.MakeWall()); r1->SetSide(East, theDoor); r1->SetSide(South, factory.MakeWall()); r1->SetSide(West, factory.MakeWall()); r2->SetSide(North, factory.MakeWall()); r2->SetSide(West, theDoor); r2->SetSide(South, factory.MakeWall()); r2->SetSide(East, factory.MakeWall());
factory是一个工厂,这个工厂可以是一个生产普通迷宫的工厂,或者是生产魔法迷宫的工厂,他们都能生产出同样的迷宫,而只是样式不同。这样就实现了迷宫的动态创建
【实现过程】
1. 实现迷宫的组件抽象类
抽象这些元素的共同点,都有enter接口,因此可以给这些组件定义一个抽象类MapSite,如下所示:
/*MapSite.h*/ #ifndef _MAPSITE_H #define _MAPSITE_H enum TYPE {MAPSITE, ROOM, WALL, DOOR}; class MapSite { public: virtual void Enter() = 0; ~MapSite(); virtual int type() = 0; }; MapSite::~MapSite() { printf("Delete MapSite/n"); } #endif
2.实现迷宫的组件
现在假设要支持两种迷宫,魔法迷宫和可墙壁可爆炸的迷宫。我们先定义房间。
/*Room.h*/ #ifndef _ROOM_H #define _ROOM_H enum Direction {North, South, East, West}; class Room : public MapSite { public: Room(); Room(int roomNo); ~Room(); type(); MapSite* GetSide(Direction) const; void SetSide(Direction dir, MapSite* pMapSite); int GetRoomNo(); virtual void Enter(); protected: int _roomNumber; private: MapSite* _sides[4]; }; Room::Room() { } Room::Room(int roomNo) { _roomNumber = roomNo; printf("Create Room NO.%d/n", roomNo); } Room::~Room() { printf("Delete Room/n"); for (int i = 0; i < 4; i++) { if (_sides[i]->type() != DOOR) // TUDO { delete _sides[i]; _sides[i] = NULL; } } } Room::type() { return ROOM; } void Room::SetSide(Direction dir, MapSite* pMapSite) { _sides[dir] = pMapSite; switch(dir) { case East: printf("Set Room NO.%d East Side/n", GetRoomNo()); break; case West: printf("Set Room NO.%d West Side/n", GetRoomNo()); break; case North: printf("Set Room NO.%d North Side/n", GetRoomNo()); break; case South: printf("Set Room NO.%d East South/n", GetRoomNo()); break; default: break; } } int Room::GetRoomNo() { return _roomNumber; } void Room::Enter() { printf("Enter Room/n"); } class EnchantedRoom : public Room { public: EnchantedRoom(int roomNo) { _roomNumber = roomNo; printf("Create EnchantedRoom NO.%d/n", roomNo); } }; class BoomedRoom : public Room { public: BoomedRoom(int roomNo) { _roomNumber = roomNo; printf("Create BoomedRoom NO.%d/n", roomNo); } }; #endif
类似的实现墙壁和门。
/*Wall.h*/ #ifndef _WALL_H #define _WALL_H class Wall : public MapSite { public: Wall(); ~Wall(); type(); virtual void Enter(); }; Wall::Wall() { printf("Create Wall/n"); } Wall::~Wall() { printf("Delete Wall/n"); } Wall::type() { return WALL; } void Wall::Enter() { printf("Enter Wall/n"); } class EnchantedWall : public Wall { public: EnchantedWall() { printf("Create EnchantedWall/n"); } }; class BoomedWall : public Wall { public: BoomedWall() { printf("Create BoomedWall/n"); } }; #endif
/*Door.h*/ #ifndef _DOOR_H #define _DOOR_H #include "Room.h" class Door : public MapSite { public: Door(Room* r1 = 0, Room * r2 = 0); ~Door(); type(); virtual void Enter(); Room* OtherSideFrom(Room *); private: Room* _room1; Room* _room2; bool _isOpen; }; Door::Door(Room* r1, Room * r2):_room1(r1), _room2(r2) { printf("Create Door between Room %d and %d/n", r1->GetRoomNo(), r2->GetRoomNo()); } Door::~Door() { printf("Delete Door/n"); } Door::type() { return DOOR; } void Door::Enter() { printf("Enter Door/n"); } #endif
3.实现工厂
工厂必须能生产门,墙和房间,因此这些应该是基类的抽象接口。
/*MazeFacory.h*/ #ifndef _MAZEFACTORY_H #define _MAZEFACTORY_H #include "Maze.h" #include "Room.h" #include "Wall.h" #include "Door.h" class MazeFactory // Abstract class { public: MazeFactory() { printf("Create MazeFactory/n"); } virtual Maze* MakeMaze() const { return new Maze; } virtual Wall* MakeWall() const = 0; virtual Room* MakeRoom(int n) const = 0; virtual Door* MakeDoor(Room* r1, Room* r2) const { return new Door(r1, r2); } }; class EnchantedMazeFactory : public MazeFactory { public: EnchantedMazeFactory() { printf("Create MazeFactory"); } virtual Room* MakeRoom(int n) const { return new EnchantedRoom(n); } virtual Wall* MakeWall() const { return new EnchantedWall(); } }; class BoomedMazeFactory : public MazeFactory { public: BoomedMazeFactory() { printf("Create MazeFactory"); } virtual Room* MakeRoom(int n) const { return new BoomedRoom(n); } virtual Wall* MakeWall() const { return new BoomedWall(); } }; #endif
4.实现迷宫类
/*MazeGame.h*/ #ifndef _MAZEGAME_H #define _MAZEGAME_H #include "Maze.h" #include "Room.h" #include "Wall.h" #include "Door.h" #include "MazeFactory.h" class MazeGame { public: MazeGame(); ~MazeGame(); Maze* CreateMaze(MazeFactory& factory); private: Maze* aMaze; Room* r1; Room* r2; Door* theDoor; }; MazeGame::MazeGame() { printf("Create MazeGame/n"); } MazeGame::~MazeGame() { delete aMaze; delete theDoor; printf("Delete MazeGame/n"); } Maze* MazeGame::CreateMaze(MazeFactory& factory) { aMaze = factory.MakeMaze(); r1 = factory.MakeRoom(1); r2 = factory.MakeRoom(2); theDoor = factory.MakeDoor(r1, r2); aMaze->AddRoom(r1); aMaze->AddRoom(r2); r1->SetSide(North, factory.MakeWall()); r1->SetSide(East, theDoor); r1->SetSide(South, factory.MakeWall()); r1->SetSide(West, factory.MakeWall()); r2->SetSide(North, factory.MakeWall()); r2->SetSide(West, theDoor); r2->SetSide(South, factory.MakeWall()); r2->SetSide(East, factory.MakeWall()); return aMaze; } #endif
5.如何使用
#include <stdio.h> #include <stdlib.h> #include "MapSite.h" #include "Room.h" #include "Wall.h" #include "Door.h" #include "Maze.h" #include "MazeGame.h" int main(void) { MazeGame *pMazeGame = new MazeGame; int tag; printf("EnchantedMaze:1/nBoomedMaze:2/n"); printf("Enter your choice:/n"); scanf("%d", &tag); EnchantedMazeFactory pEnchangtedFactory; BoomedMazeFactory pBoomedMazeFactory; switch(tag) { case 1: pMazeGame->CreateMaze(pEnchangtedFactory); case 2: pMazeGame->CreateMaze(pBoomedMazeFactory); } delete pMazeGame; return 0; }
【总结】
抽象工厂在抽象工厂类中定义接口,子类中实现父类的方法,不同的配置方式就有多个子类去实现,而客户只使用父类中提供的接口,通过传参数的方式在客户类中决定用那个具体的工厂去实例化对象。
下图是本文中举例的图示,图中MazeFactory作为父类限定了接口,可以是抽象类,也可是具体类,如是具体类,则可以定义一个最基本的功能接口,并可以在Client类MazeGame中初实例化。
客户类MazeGame接收一个MazeFactory引用参数来实例化一个产品,通过传入具体类的参数在运行时刻决定应该实例化那一个类。
Room和Wall作为组件使用来构建具体的工厂类。
这个模式的缺点在于难以支持新种类的产品,每增加一个新的功能就要修改抽象工厂类的接口。
抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口。