提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道或关心实际产出的具体产品是什么。这样一来,客户就能从具体的产品中被解耦。
在以下情况可以使用 Abstract Factory 模式
抽象工厂模式 UML 图 a)
——声明一个创建抽象产品对象的操作接口。
——实现创建具体产品对象的操作。
——为一类产品对象声明一个接口。
——定义一个将被相应的具体工厂创建的产品对象。
——实现 AbstractProduct 接口。
在这里指的是 ProductA1,ProductA2,ProductB1,ProductB2。
——仅适用由 AbstractFactory 和 AbstractProduct 类声明的接口。
一般而言,有多少个产品等级结构,就会在工厂角色中发现多少个工厂方法。每一个产品等级结构中有多少个具体的产品,就有多少个产品族,也就会在工厂等级结构中发现多少个具体工厂。
《设计模式:可复用面向对象软件的基础》中一书中,给出了一个应用工厂方法设计迷宫的例子,这里修改为应用抽象工厂模式来实现。
首先,给出了枚举类型 Direction 表明东南西北四个方向,定义抽象产品 AbstractProduct 接口,包含一个纯虚函数 Enter,用于在迷宫间移动位置。
- enum Direction {North, South, East, West};
- //AbstractProduct
- class MapSite{
- public:
- virtual void Enter() = 0;
- };
接下来,基于抽象产品接口 MapSite 实现 ConcreteProduct 具体产品类:
定义迷宫房间 Room 类(ProductA);
- //ProductA
- class Room : public MapSite{
- public:
- Room(int roomNo);//初始化房间号
- MapSite *GetSide(Direction) const;//获取当前房间四周具体产品ConcreteProduct类MapSite
- void SetSide(Direction, MapSite*);//设置当前房间四周具体产品ConcreteProduct类MapSite
- virtual void Enter();
- private:
- MapSite* _side[4];
- int _roomNumber;
- };
定义迷宫的墙 Wall 类(ProductB);
- //ProductB
- class Wall : public MapSite{
- public:
- Wall();
- virtual void Enter();
- };
定义迷宫的门 Door 类(ProductC);
- //ProductC
- class Door : public MapSite{
- public:
- Door(Room * = 0, Room * = 0);//初始化门两边的房间
- virtual void Enter();
- Room* OtherSideFrom(Room *);
- private:
- Room * _room1;
- Room * _room2;
- bool _isOpen;
- };
定义迷宫 Maze 类(ProductD);
- //ProductD
- class Maze{
- public:
- Maze();
- void AddRoom(Room *);
- Room* RoomNo(int) const;
- private:
- //...
- };
在完成抽象产品接口和具体产品类定义后,接下来定义抽象工厂接口 AbstractFactory 类 MazeFactory,对外提供接口,用于生成上述定义的具体产品类 ProductA——Room,ProductB——Wall,ProductC——Door,ProductD——Maze。
- //AbstractFactory
- class MazeFactory{
- public:
- MazeFactory();
- virtual Maze* MakeMaze() const
- {return new Maze;}
- virtual Wall* MakeWall() const
- {return new Wall;}
- virtual Room* MakeRoom(int n) const
- {return new Room(n);}
- virtual Door* MakeDoor(Room* r1, Room* r2) const
- {return new Door(r1,r2);}
- };
在定义好抽象工厂接口、抽象产品接口、具体产品后,就可以由 Client 来动态创建和处理各个对象的关系,这里创建一个包含两个房间的简单迷宫。
- //Client
- 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;
- }
如果需要创建带各种特效或者属性的 Room、Door、Wall ,只需要继承具体产品类 ProductA、ProductB、ProductC、ProductD,以及抽象工厂 AbstractFactory 接口 MazeFactory。
如果要生成魔法迷宫,只需要继承 MazeFactory 生成 ConcreteFactory 类 EnchantedMazeFacotry,
- //ConcreteFactory
- class EnchantedMazeFactory: public MazeFactory {
- public: EnchantedMazeFacotry();
- 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;
- };
调用 CreateMaze 就可以生成带魔法房间和符咒门的迷宫了。。。
- //Client
- MazeFactory * enchantedMazeFac = new EnchantedMazeFactory;
- Maze * enchantedMaze = CreateMaze(enchantedMazeFac);
同理还可以,由爆炸工厂生成带爆炸门和爆炸房间的迷宫。。。
- Wall * BombedMazeFactory: :MakeWall() const {
- return new BombedWall;
- }
- Room * BombedMazeFactory: :MakeRoom(int n) const {
- return new RoomWithABomb(n);
- }
如果嫌上面例子麻烦, 下面这个例子(截图自 wikipedia 官网),简单明了的阐释了抽象工厂模式:
优点:
1、抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。
2、当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
3、增加新的具体工厂和产品族很方便,无须修改已有系统,符合 "开闭原则"。
缺点:
增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对 "开闭原则" 的支持呈现倾斜性。
参考资料:
a) https://en.wikipedia.org/wiki/Abstract_factory_pattern
b) 《设计模式:可复用面向对象软件的基础》
c) http://www.runoob.com/design-pattern/abstract-factory-pattern.html
d) https://zh.wikipedia.org/wiki / 抽象工厂
来源: http://www.cnblogs.com/chenyangchun/p/7183795.html