工厂方法的目的很明确就是定义一个用来创建对象的接口,但是他不直接创建对象,而由他的子类来创建,这样一来就将创建对象的责任推迟到了该接口的子类中,创建什么类型的对象由子类来决定,而创建对象的时间由接口来定。因此该模式可以在如下几种情况下使用: 1 、 a class can’t predict the class of objects it must create.2 、 a class wants its subclasses to specify the objects it creates.3 、 classes delegate responsibility to one of several helper subclasses,but do not know whick helper subclass is the delegate. 在这里我们举一个面包房的例子:面包房的面包机好比一个抽象产品,它是一般的面包,那么我们可以通过特殊的加工工艺使它变成不同种类的面包比如:可以是中国式的面包或者是美国的面包等等。这些特殊种类的面包就是我们的具体产品。而做面包的人(也许是自己)就是 Creator (创建者),我们使用我们的方法(工厂方法)来做我们想要的面包。这里的灵活性是不言而喻的,使用面包机可以让我们有更多的选择,只有我们在需要的时候才决定吃那一种面包,这不是很好的一件事情吗,如果没有面包机可能我们只能吃中国面包了。下面我们在举几个我们非常熟悉的 FCL ( .NET Framrwork Class Labrarly )中的例子, IEnumerable 和 Ienumerator 就是一个 Creator 和一个 Product 。另一个例子是: WebRequest 是 .NET Framework 的用于访问 Internet 数据的请求 / 响应模型的抽象(在 Visual Basic 中为 MustInherit )基类。使用该请求 / 响应模型的应用程序可以用协议不可知的方式从 Internet 请求数据。在这种方式下,应用程序处理 WebRequest 类的实例,而协议特定的子类则执行请求的具体细节。请求从应用程序发送到某个特定的 URI ,如服务器上的 Web 页。 URI 从一个为应用程序注册的 WebRequest 子代列表中确定要创建的适当子类。注册 WebRequest 子代通常是为了处理某个特定的协议(如 HTTP 或 FTP ),但是也可以注册它以处理对特定服务器或服务器上的路径的请求。( WebRequest 摘自 MSDN ),这是一种带参数的工厂方法。在 FCL 里面工厂方法是一种最常见的模式应用。
以上据了一个简单的例子(面包),不知道是不是贴切,我不想重新描写一次该模式,但是再有必要的时候我会加以强调的,下面就用创建型模式的结构图来展现这个模式。

这次我想实现的就是书上的例子,首先我们需要看看我们的环境(上下文),我们为了实现一个迷宫的创建环境所以,这个迷宫的简单结构如下图所示:

为了实现工厂模式来创建迷宫我们有一下的类图,从图中可以看出 Creator ( MazeGame )里面有很多的工厂方法,用户可以通过它来得到不同的房子(组成迷宫的组件),但是也许我们不知道我们的 MazeGame 是要创建什么样的 Room 所以我们可以定义一些子类(现在有两个)用来实现不同的创建工作, Room 是组成我们的迷宫的最常见的房子, MazeGame 可以产生这样大的房子但是为了满足不同的要求我们可以定义一些特殊的房子比如:带炸弹的房子(是指墙上有炸弹)和可以施魔法的房子(可以对门施魔法)。这样我们可以不改变原来的接口,用 MazeGame 的子类来完成这些特殊的创建工作,就像下面的图中所表示的一样。

我们把创建的细节推迟到了子类去实现,这样很大程度上提高了程序的韧性,如果我们想创建一个新的房子或者新的门我们可以在不改变原来代码的情况下添加以个 MazeGame 子类来实现这个想法。但是我觉得这有一个问题为了创建一个新的房子我们必须创建一个新的子类来满足这个要求,这样看起来好像是一对一对的平行的样子。这样会使我们的 Creator 的子类越来越多,我们可以使用带参数的工厂方法减少这种类的繁殖,下面实现的代码并没有实现这种带参数的工厂方法,网友们可以自己实现。我们只要给 MakeWall 和 MakeDoor 传递一个参数来确定创建什么样的 Wall 和 Door ,不过我们需要在默认的 MazeGame 里相应的方法写重裁版本。
好了废话就不多说了, GOF 说的比我清楚多了(其实我也是从他们那里学的 J )。下面来实现我们的迷宫,代码如下:
首先实现我们的环境
using System;
using System.Collections;
// 该命名空间中是一些运行实例德的环境包括 Maze 、 Door 等等
namespace CommonObject{
// 所有的迷宫构件的基类里面有一个方法用来显示当前构件的信息
public class MapSite{
public virtual string Enter(){
return string.Empty;
}
}
// 墙是组成迷宫的构件之一,这里是一个很一般的墙(没有炸弹)
public class Wall : MapSite{
public override string Enter(){
return "This is a Wall.";
}
public Wall(){}
}
// 门也是迷宫的组成部分之一,这也是一个很普通的门(不能施魔法)
public class Door : MapSite{
public override string Enter(){
return "This is a Door.";
}
// 门是在两个房子之间的构件所以构造函数包含两个房子
// 说明是哪两个房子之间的门
public Door(Room roomFrom,Room roomTo){
this.m_Room1 = roomFrom;
this.m_Room2 = roomTo;
}
// 让我们有机会可以从门进入另一个房子,将会得到
// 另一个房子的引用
public Room OtherSideFrom(Room roomFrom){
if(this.m_Room1 == roomFrom)
return this.m_Room2;
else
return this.m_Room1;
}
// 这是一些私有的变量
private Room m_Room1;
private Room m_Room2;
// 描述门的状态默认所有的新门都是关的
private bool m_IsOpen = false;
// 提供一个公共访问的访问器
public bool IsOpen{
set{this.m_IsOpen = value;}
get{return this.m_IsOpen;}
}
}
// 房间是组成迷宫的基本单位
public class Room : MapSite
{
// 枚举类型表示房子的面
public enum Sides{
North = 0,
East,
South,
West
}
public override string Enter(){
return "This is a Room.";
}
// 构造函数,为了可以区分房间我们给每一个房间加一个标示
public Room(int roomNumber){
this.m_roomNumber = roomNumber;
}
// 设置房子的面,房子有 4 个面组成,因为我们现在不知道每个面
// 的具体类型(门?墙?)所以我们用 MapSite 类型。
public void SetSide(Sides side,MapSite sideMap){
this.m_side[(int)side] = sideMap;
}
// 得到指定的面,同样我们不知道得到的是哪一个面
// 所以我们用 MapSite 返回结构
public MapSite GetSide(Sides side){
return this.m_side[(int)side];
}
// 一些私有成员 房号
protected int m_roomNumber;
// 房子有 4 个面
protected const int m_Sides = 4;
// 用一个 1 维的 MapSite 数组存储未知类型的面(墙或门)
protected MapSite[] m_side = new MapSite[m_Sides];
}
// 带炸弹的房子
public class BombedRoom : Room{
public BombedRoom(int roomNumber) : base(roomNumber){}
}
// 带迷宫的房子
public class EnchantedRoom : Room{
public EnchantedRoom(int roomNumber,Spell spell) : base(roomNumber){}
}
// 这是一个特殊的墙--带炸弹的
public class BombedWall : Wall{}
// 这是一个可以施魔法的门
public class EnchantedDoor : Door{
public EnchantedDoor(Room roomFrom,Room roomTo) : base(roomFrom,roomTo){}
}
// 这就是我们的迷宫了
public class Maze{
private ArrayList m_Rooms = new ArrayList();
public Room RoomNumber(int roomNumber){
return (Room)this.m_Rooms[roomNumber];
}
public void AddRoom(Room room){
this.m_Rooms.Add(room);
}
}
// 魔法类
public class Spell{}
}
接下来是工厂方法的实现:
using System;
using System.Collections;
namespace FactoryMethod_Example
{
// 加入 MazeContext
using CommonObject;
// 实现了一些工厂方法的 Creator 类
public class MazeGame
{
// 要返回给 Client 的对象
private Maze m_Maze = null;
// 一个访问器用来得到 maze
public Maze Maze
{
get
{
&