意图
提供一个创建一系列相关的或相互依赖对象的接口,而无需指定它们具体的类。
适用性
- 一个系统要独立于它的产品的创建、组合和表示时;
- 一个系统要由多个产品系列中的一个来配置;
- 当要强调一个系列相关的产品对象的设计以便进行联合使用时;
- 当要提供一个产品类库,而只想显示它们的接口而不是实现。
结构
- AbtractFactory
声明一个创建抽象产品系列对象的操作接口。
- ConcreteFactory
实现创建具体产品系列对象的操作。
- AbstractProduct
为一种类产品对象声明一个接口。
- ConcreteProduct
定义一个将被相应的具体工厂创建的产品对象。
实现AbstractProduct接口。
- Client
仅使用AbstractFactory和AbtractProcduct类声明的接口。
这里ProductA,ProductB的区别称为产品种类不同,1、2的区别称为产品系列不同。
协作
- 通常在运行时刻创建一个ConcreteFactory类的实例。这一个具体的工厂创建具有特定实现的的产品对象。为创建不同的产品对象,客户应使用不同的具体工厂。
- AbstractFactory将产品对象的创建延迟到他的ConcreteFactory子类。
效果
优点
1) 它分离了具体的类
Abstract Factory模式使客户只有在创建工厂时需要指定具体的类,而后都不需要接触具体的工厂和产品类。因为每个工厂封装了创建产品对象的过程,它将客户与类的实现分离。创建工厂对象后,客户就可以通过AbstractFactory和AbstractProduct的接口操作实例,并且产品的具体类名不出现在客户代码中。客户相当于只有在选择哪个具体工工厂处需要关心需要哪个产品系列,而后的产品创建与使用都实现了解耦。
2)它使得易于交换产品系列
一个具体工厂类在一个应用中仅出现了一次,即在它初始化的时候。这使得改变一个应用的产品系列变得很容易。它只需要改变初始化不同具体的工厂,即可使用不同的产品配置,而抽象工厂中包含了创建一个完整的产品系列的接口,所以整个产品系列会随着初始化具体工厂类的选择不同而立刻改变,不需要再修改其他的地方。
3)它有利于产品的一致性
当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,比如使用ConcreteFactroy1时就只能使用A1和B1的产品,不能使用A2和B2的产品,它可以很好的将应用能使用的产品限定在某个系列上,避免应用使用错误。
并且增加一个产品系列是很容易的,只要再通过继承创建一个ConcreteFactory3和对应的ProductA3、ProductB3,对之前的代码都不会有影响,遵守了开闭的原则。
缺点
1)难以增加新种类的产品
前面说过增加新系列是容易的,但是增加新的产品种类,即要改变应用使用的产品个数就会变的困难。因为AbstractFactory接口确定了被创建的产品集合,如果要增加新的产品种类ProductC就要修改AbstractFactory类和它所有的子类。
实现
实现Abstract Factory模式一些有用的技术:
1)将工厂作为单件
一个应用中一般每个产品系列只需要一个ConcreteFactory的实例。可以用这个具体工厂实例一直创建同一系列的产品对象。因此工厂通常最好实现为一个Singleton。
2)创建产品
AbstractFactroy仅声明了一个创建产品的接口,真正创建产品是由ConcreteFactory实现的。最通常的一个办法是为每个产品种类定义一个工厂方法(Factory Method)。一个具体的工厂将为每个产品重定义该工厂方法以指定产品。虽然这样的实现很简单,但却要求每个产品系列都要有一个新的具体工厂子类即使这些产品系列差别很小。
如果有多个可能的产品系列,具体工厂也可以使用原型(Prototype)模式来实现。具体工厂使用产品系列中每个产品的原型实例来初始化,通过复制它的原型来创建新的产品。使用基于原型的方法,不是每个新的产品系列都需要一个新的具体工厂类。
3)定义可扩展的工厂
Abstract Factory通常为每一种它可以生产的产品定义一个操作,产品的种类被硬编码在操作型构中,前面已经提过增加一种新的产品就要修改AbstactFactory的接口以及所有与其相关的类。一个更灵活的但不太安全的设计师给创建对象的操作增加一个参数,通过该参数指定要创建的对象的种类,比如AbstractFactory只需要一个make
操作和一个指示要创建对象种类的参数即可返回对应的具体产品。但是这里存在一个需要类型强制转换的问题,因此在强类型语言中是不安全的。
示例
功能:一个可以生成不同迷宫的游戏,每个迷宫由若干房间组成,每个房间有墙和门。这些房间,墙,门都是可以不一样的,即这三个是Product。
//Maze.hpp
//普通迷宫
#pragma once
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>
enum Direction
{
North,
South,
East,
West
};
const std::map<Direction, std::string> direction = { {North,"North"}, {South,"South"}, {East,"East"}, {West,"West"} };
//迷宫组件抽象类
class MapSite
{
public:
virtual void Enter() = 0;
};
//房间
class Room : public MapSite
{
public:
Room(int roomNo) : m_roomNo(roomNo)
{
std::cout << "new Room with number " << roomNo << std::endl;
}
MapSite* GetSide(Direction d) const
{
return m_sides[d];
}
void SetSide(Direction d, MapSite* ms)
{
m_sides[d] = ms;
std::cout << "Set Room " << m_roomNo << " " << direction.at(d) << " Side with a ";
ms->Enter();
}
int GetRoomNo() const
{
return m_roomNo;
}
virtual void Enter()
{
std::cout << "Room" << std::endl;
//...
}
private:
MapSite * m_sides[4];
int m_roomNo;
};
//墙
class Wall : public MapSite
{
public:
Wall()
{
std::cout << "new Wall" << std::endl;
}
virtual void Enter()
{
std::cout << "Wall" << std::endl;
//...
}
};
//门
class Door : public MapSite
{
public:
Door(Room* r1, Room* r2) : m_room1(r1), m_room2(r2), m_isOpen(false)
{
std::cout << "new Door with room " << m_room1->GetRoomNo() << " and room " << m_room2->GetRoomNo() << std::endl;
}
virtual void Enter()
{
std::cout << "Door" << std::endl;
//...
}
Room* OtherSideFrom(Room* r)
{
if (r == m_room1)
return m_room2;
else if (r == m_room2)
return m_room1;
else
return nullptr;
}
private:
Room * m_room1;
Room * m_room2;
bool m_isOpen;
};
//迷宫
class Maze
{
public:
Maze()
{
std::cout << "new Maze" << std::endl;
}
void AddRoom(Room* r)
{
std::cout << "Maze add room " << r->GetRoomNo() << std::endl;
m_pRooms.push_back(r);
}
Room* RoomNo(int num) const
{
auto it = std::find_if(m_pRooms.cbegin(), m_pRooms.cend(), [num](Room* r) {
if (r->GetRoomNo() == num)
return true;
else
return false;
});
if (it != m_pRooms.cend())
return *it;
else
return nullptr;
}
private:
std::vector<Room*> m_pRooms;
};
//EnchantedMaze.hpp
//有魔法的迷宫
#pragma once
#include "Maze.hpp"
//魔法
class Spell
{
public:
Spell()
{
std::cout << "new Spell" << std::endl;
}
};
class EnchantedRoom : public Room
{
public:
EnchantedRoom(int roomNo, Spell* sp) : Room(roomNo), m_sp(sp)
{
std::cout << "new Enchanted Room" << std::endl;
}
private:
Spell * m_sp;
};
class DoorNeedingSpell : public Door
{
public:
DoorNeedingSpell(Room* r1, Room* r2) : Door(r1, r2)
{
std::cout << "new DoorNeedingSpell" << std::endl;
}
};
//BombedRoom.hpp
//带有炸弹的迷宫
#pragma once
#include "Maze.hpp"
class RoomWithABomb : public Room
{
public:
RoomWithABomb(int roomNo) : Room(roomNo)
{
std::cout << "new RoomWithABomb" << std::endl;
}
};
class BombedWall : public Wall
{
public:
BombedWall() : Wall()
{
std::cout << "new BomedWall" << std::endl;
}
};
//AbstractFactory.hpp
//迷宫工厂
#pragma once
#include "Maze.hpp"
#include "EnchantedMaze.hpp"
#include "BombedMaze.hpp"
//普通迷宫工厂
//既是抽象工厂也是具体工厂
class MazeFactory
{
public:
MazeFactory()
{
std::cout << "new MazeFactory" << std::endl;
}
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);
}
};
//施了魔法的迷宫工厂,替换了普通迷宫里面的Room和Door
class EnchantedMazeFactory : public MazeFactory
{
public:
EnchantedMazeFactory()
{
std::cout << "new EnchantedMazeFactory" << std::endl;
}
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;
}
};
//有炸弹的迷宫工厂,替换了Room和Wall
class BombedMazeFactory : public MazeFactory
{
public:
BombedMazeFactory()
{
std::cout << "new BombedMazeFactory" << std::endl;
}
virtual Room* MakeRoom(int n) const
{
return new RoomWithABomb(n);
}
virtual Wall* MakeWall() const
{
return new BombedWall();
}
};
//MazeGame.hpp
//迷宫游戏, 用于产生不同类型的迷宫
#pragma once
#include "Maze.hpp"
#include "AbstractFactory.hpp"
class MazeGame
{
public:
//没有使用工厂来创建迷宫,如果要换产生另一种类型的迷宫,就要重新写
Maze* CreateMaze()
{
Maze* pMaze = new Maze;
Room* r1 = new Room(1);
Room* r2 = new Room(2);
Door* pDoor = new Door(r1, r2);
pMaze->AddRoom(r1);
pMaze->AddRoom(r2);
r1->SetSide(North, new Wall());
r1->SetSide(East, pDoor);
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, pDoor);
std::cout << "return Maze" << std::endl;
return pMaze;
}
//使用迷宫工厂创建具体的迷宫,创建另一种迷宫,只要传入的factory不同即可
Maze* CreateMaze(MazeFactory& factory)
{
Maze* pMaze = factory.MakeMaze();
Room* r1 = factory.MakeRoom(1);
Room* r2 = factory.MakeRoom(2);
Door* pDoor = factory.MakeDoor(r1, r2);
pMaze->AddRoom(r1);
pMaze->AddRoom(r2);
r1->SetSide(North, factory.MakeWall());
r1->SetSide(East, pDoor);
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, pDoor);
return pMaze;
}
};
//main.cpp
//使用不同方法创建迷宫
#include "MazeGame.hpp"
int main()
{
MazeGame game;
//使用传统方法创建迷宫
//只能创建一种迷宫,如果要创建另外一种迷宫,需要修改CreateMaze()代码
std::cout << "== Create normal Maze without AbstractFactory ==" << std::endl;
Maze* pMaze = game.CreateMaze();
//使用普通迷宫工厂,创建普通迷宫
std::cout << "== Create normal Maze with AbstractFactroy ==" << std::endl;
MazeFactory factory1;
Maze* pMaze1 = game.CreateMaze(factory1);
//换一种工厂就可以创建另外一种迷宫,无需修改CreateMaze()
std::cout << "== Create Enchanted Maze ==" << std::endl;
EnchantedMazeFactory factory2;
Maze* pMaze2 = game.CreateMaze(factory2);
//增加一种迷宫类型,只要再创建一个工厂和一个的产品系列,不需要修改原来的代码
std::cout << "== Create Bombed Maze ==" << std::endl;
BombedMazeFactory factory3;
Maze* pMaze3 = game.CreateMaze(factory3);
return 0;
}
== Create normal Maze without AbstractFactory ==
new Maze
new Room with number 1
new Room with number 2
new Door with room 1 and room 2
Maze add room 1
Maze add room 2
new Wall
Set Room 1 North Side with a Wall
Set Room 1 East Side with a Door
new Wall
Set Room 1 South Side with a Wall
new Wall
Set Room 1 West Side with a Wall
new Wall
Set Room 2 North Side with a Wall
new Wall
Set Room 2 East Side with a Wall
new Wall
Set Room 2 South Side with a Wall
Set Room 2 West Side with a Door
return Maze
== Create normal Maze with AbstractFactroy ==
new MazeFactory
new Maze
new Room with number 1
new Room with number 2
new Door with room 1 and room 2
Maze add room 1
Maze add room 2
new Wall
Set Room 1 North Side with a Wall
Set Room 1 East Side with a Door
new Wall
Set Room 1 South Side with a Wall
new Wall
Set Room 1 West Side with a Wall
new Wall
Set Room 2 North Side with a Wall
new Wall
Set Room 2 East Side with a Wall
new Wall
Set Room 2 South Side with a Wall
Set Room 2 West Side with a Door
== Create Enchanted Maze ==
new MazeFactory
new EnchantedMazeFactory
new Maze
Cast Spell
new Room with number 1
new Enchanted Room
Cast Spell
new Room with number 2
new Enchanted Room
new Door with room 1 and room 2
new DoorNeedingSpell
Maze add room 1
Maze add room 2
new Wall
Set Room 1 North Side with a Wall
Set Room 1 East Side with a Door
new Wall
Set Room 1 South Side with a Wall
new Wall
Set Room 1 West Side with a Wall
new Wall
Set Room 2 North Side with a Wall
new Wall
Set Room 2 East Side with a Wall
new Wall
Set Room 2 South Side with a Wall
Set Room 2 West Side with a Door
== Create Bombed Maze ==
new MazeFactory
new BombedMazeFactory
new Maze
new Room with number 1
new RoomWithABomb
new Room with number 2
new RoomWithABomb
new Door with room 1 and room 2
Maze add room 1
Maze add room 2
new Wall
new BomedWall
Set Room 1 North Side with a Wall
Set Room 1 East Side with a Door
new Wall
new BomedWall
Set Room 1 South Side with a Wall
new Wall
new BomedWall
Set Room 1 West Side with a Wall
new Wall
new BomedWall
Set Room 2 North Side with a Wall
new Wall
new BomedWall
Set Room 2 East Side with a Wall
new Wall
new BomedWall
Set Room 2 South Side with a Wall
Set Room 2 West Side with a Door
这个示例中MzeFactory不是一个抽象类,它既作为AbstractFactory也作为ConcreteFactory。这是AbstractFactory另一种通常的实现。因为MazeFactory是一个完全由工厂方法组成的具体类,通过生成一个子类并重定义需要改变的操作,很容易生成一个新的MazeFactory。