在GOF所著的《设计模式》一书中,描述了Builder模式的意图:“将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。”按照封装变化的原理,Builder模式实则是封装对象创建的变化,但它与Factory Method模式、Abstract Factory模式不同的是,所谓对象的创建,主要是指对象内部构件的创建。形象地说,Builder模式就好似生产线的装配工人,可以接收多种方式与顺序组装各种零部件。本章,将给出我参与设计与开发的CIMS系统中的一个需求,详细讲解Builder模式的应用。
1 需求分析——装配设备对象
在CIMS(Computer Integrated Manufacture System)项目中,有这样一个需求。系统中需要创建Equipment对象,这些对象由Machine对象和多个Port对象组成。Port对象包含两种类型:Input与Output。在Machine对象中,定义了PortType属性,它的值与Port对象的类型相对应。从目前的需求来看,Equipment对象,存在如下三种组成情况。
— 一个Machine对象,一个Input类型的Port对象;
— 一个Machine对象,一个Output类型的Port对象;
— 一个Machine对象,一个Input类型的Port对象,一个Output类型的Port对象。
客户方不排除将来有增加新的Equipment组合的可能。
需求清晰而简单,我们可以非常容易地识别出Port、Machine、Equipment对象。Port类的定义如下:
public abstract class Port
![]()
...{
![]()
public abstract void Transfer();
![]()
}
![]()
![]()
![]()
public class InputPort:Port
![]()
...{
![]()
public override void Transfer()
![]()
...{
![]()
Console.WriteLine("Input");
![]()
}
![]()
}
![]()
public class OutputPort:Port
![]()
...{
![]()
public override void Transfer()
![]()
...{
![]()
Console.WriteLine("Output");
![]()
}
![]()
}
![]()
Port对象由于类型的不同,方法Transfer会有不同的实现,因此,我们定义了一个抽象Port类,然后定义其子类InputPort和OutputPort,分别代表两种不同类型的Port。
Machine类的定义相对比较简单,如下所示。
public class Machine
![]()
...{
![]()
public Machine() ...{ }
![]()
public Machine(string name)
![]()
...{
![]()
m_name = name;
![]()
}
![]()
![]()
![]()
private string m_name;
![]()
private string m_portType;
![]()
![]()
![]()
public string Name
![]()
...{
![]()
get ...{ return m_name; }
![]()
set ...{ m_name = value; }
![]()
}
![]()
![]()
![]()
public string PortType
![]()
...{
![]()
get ...{ return m_portType; }
![]()
set ...{ m_portType = value; }
![]()
}
![]()
![]()
![]()
public void Run()
![]()
...{
![]()
Console.WriteLine("The machine {0} is running!", m_name);
![]()
}
![]()
由于Equipment对象可能会包含多个Port,因此Equipment类引入集合对象来保存Port对象,并利用泛型限制集合元素的类型,通过强类型的方式避免强制转换时可能出现的异常或错误。
public class Equipment
![]()
...{
![]()
public Equipment()
![]()
...{
![]()
m_list = new List<Port>();
![]()
}
![]()
![]()
![]()
private Machine m_machine;
![]()
private List<Port> m_list;
![]()
private string m_name;
![]()
![]()
![]()
public Machine Machine
![]()
...{
![]()
get ...{ return m_machine; }
![]()
set ...{ m_machine = value; }
![]()
}
![]()
public List<Port> PortsList
![]()
...{
![]()
get ...{ return m_list; }
![]()
}
![]()
public string Name
![]()
...{
![]()
get ...{ return m_name; }
![]()
set ...{ m_name = value; }
![]()
}
![]()
![]()
![]()
public void AddPort(Port port)
![]()
...{
![]()
m_list.Add(port);
![]()
}
![]()
public void RemovePort(Port port)
![]()
...{
![]()
m_list.Remove(port);
![]()
}
![]()
public void Run()
![]()
...{
![]()
Console.WriteLine("The Equipment {0} is running as below...",m_name);
![]()
foreach (Port port in m_list)
![]()
...{
![]()
port.Transfer();
![]()
}
![]()
m_machine.Run();
![]()
}
![]()
}
![]()
Equipment的Run方法会遍历m_list中的元素,并执行Port类型的Transfer方法,然后再执行Machine对象的Run方法。
