1、意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
2、别名
Kit
3、适用性
以下情况下可以使用此模式:
• 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
• 这个系统有多于一个的产品族,而系统只消费其中某一产品族。
• 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
• 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
4、从上一篇的例子引申出来的一个问题:
上一篇我们说到了使用硬编码的方法创建一个迷宫。这次我们需要将上面的代码复用,来创建一个被施了魔法的迷宫,这个施了魔法的迷宫由施过魔法的房间,需要符咒才能通过的门以及普通的墙壁构成。那么我们来看抽象工厂是如何方便解决这个问题的。
5、例子的结构图示、代码及注释
在这里,构建迷宫的元素在MazeFactory中创建,对应的代码如下:
namespace My.Reading.DesignPatterns.AbstractFactory
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
public class MazeFactory
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public MazeFactory()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
public virtual Maze MakeMaze()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return new Maze();
}
public virtual Room MakeRoom(int roomNo)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return new Room(roomNo);
}
public virtual Wall MakeWall()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return new Wall();
}
public virtual Door MakeDoor(Room r1, Room r2)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return new Door(r1, r2);
}
}
}
注意这里并没有将MazeFactory声明为abstract,因为在这个例子中,MazeFactory既作为AbstractFactory,EnchantedMazeFactory就是继承自它;同时又作为ConcreteFactory,它仍然需要创建普通的Maze对象。那么同样它的派生类的代码如下:
namespace My.Reading.DesignPatterns.AbstractFactory
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
public class EnchantedMazeFactory : MazeFactory
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public EnchantedMazeFactory()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
public override Room MakeRoom(int roomNo)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return new EnchantedRoom(roomNo);
}
public override Door MakeDoor(Room r1, Room r2)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return new DoorNeedingSpell(r1, r2);
}
}
}
由于Wall使用的仍然是普通的Wall,因此没有必要覆盖抽象工厂的虚方法。同时为了方便,没有使用EnchantedMaze而仍然使用Maze,只是在添加Room和Door的时候添加的是EnchantedRoom和DoorNeedingSpell。
Maze由于在上一篇已经定义过了,我们不需要改变原有的代码,我们现在只需要将与原来不同的类EnchantedRoom和DoorNeedingSpell重新定义。代码如下:
namespace My.Reading.DesignPatterns.AbstractFactory
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
public class DoorNeedingSpell : Door
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public DoorNeedingSpell(Room r1, Room r2) : base(r1, r2)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
public override void Enter()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
throw new NotImplementedException();
}
public override void ShowSelfInformation()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Console.WriteLine("I am the door that needs spell in namespace AbstractFactory!");
}
}
public class EnchantedRoom : Room
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public EnchantedRoom(int roomNo) : base(roomNo)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
public override void Enter()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
throw new NotImplementedException();
}
public override void ShowSelfInformation()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Console.WriteLine("I am the enchanted room in namespace AbstractFactory!");
}
}
}
那么,现在我们使用工厂作为参数来构建迷宫。如果我们使用普通迷宫,则传入创建普通迷宫的工厂;如果我们使用施了魔法的迷宫,则传入创建施了魔法魔法的迷宫的工厂。
namespace My.Reading.DesignPatterns.AbstractFactory
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
public class MazeGame
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public MazeGame()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
public Maze CreateMaze(MazeFactory factory)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Maze aMaze = factory.MakeMaze();
Room r1 = factory.MakeRoom(1);
Room r2 = factory.MakeRoom(2);
Door theDoor = factory.MakeDoor(r1, r2);
aMaze.AddRoom(r1);
aMaze.AddRoom(r2);
r1.SetSide(Direction.North, factory.MakeWall());
r1.SetSide(Direction.East, theDoor);
r1.SetSide(Direction.South, factory.MakeWall());
r1.SetSide(Direction.West, factory.MakeWall());
r2.SetSide(Direction.North, factory.MakeWall());
r2.SetSide(Direction.East, factory.MakeWall());
r2.SetSide(Direction.South, factory.MakeWall());
r2.SetSide(Direction.West, theDoor);
return aMaze;
}
}
} 这样我们便避开了使用硬编码的方法,使得程序更容易扩展。
6、实现抽象工厂的一些有用技术:
1)将工厂作为单件:一个应用中一般每个产品系列只需一个ConcreteFactory,因此工厂最好实现为一个Singleton。
2)创建产品。抽象工厂仅仅声明了创建产品的接口,真正创建是由子类实现。那么通常有可能为每个产品定义一个工厂方法。但如果是有多个可能的产品系列,具体工厂也可以使用Phototype模式来实现。其实这样的创建产品主要是为了削弱抽象工厂对产品族的约束。
PS:原文是用C++描述的,很多地方只是示意性的代码。因此有些地方可能会有错误,欢迎指正。
此文章对应的代码下载:
点击此处下载