Builder模式也是创建型模式中的一种。主要用来应对构成复杂但构成结构和顺序相对稳定的对象的创建工作。目的是省去在对象发生变化时,需要修改代码中每一处对象创建的地方,应用这种模式,可以在一个复杂对象的内部结构(由许多其他子对象构成的结构)发生变化时,仅需要对对象有所修改,而复杂对象与其他的对象之间的关系、行为不需要进行修改。
其实这就是这个创建型模式应用的动机:在软件系统中,有时面临着一个复杂对象的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
GOF:
将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
下面举一个例子,来说明这种应用,比如说买电脑中的装机,根据消费者不同的需求,我们要提供不同品牌不同配置的电脑。
装机器就像Builder模式描述的一样,组装成机器的方法(即用子对象构建对象的算法)是稳定的,但是根据客户的需求可能对机器现有配置要求升级,则只是子对象发生了变化,但是这不影响极其的组装方法。
定义组装成电脑需要的设备,这里为了代码量少一些,简化了电脑的构建,粗略的分为CPU、主板、内存、硬盘、键盘、鼠标。首先构建它们对应的抽象类。
// 电脑
public abstract class Computer
{
}
// CPU
public abstract class CPU
{
}
// 主板
public abstract class Mainboard
{
}
// 内存
public abstract class Memory
{
}
// 硬盘
public abstract class Harddisk
{
}
// 键盘
public abstract class Keyboard
{
}
// 鼠标
public abstract class Mouse
{
}
然后,就可以定义构建组装电脑需要的配件的抽象方法。
public abstract class ComputerBuilder
{
public abstract void BuildCPU();
public abstract void BuildMainboard();
public abstract void BuildMemory();
public abstract void BuildHarddisk();
public abstract void BuildKeyboard();
public abstract void BuildMouse();
public abstract Computer GetComputer();
}
这里仅提供了一种品牌机的标准配置IBM。这时就可以来构建我们实际需要的IBM配件(实际子对象)了。
// IBM电脑
public class IBMComputer : Computer
{
}
// IBMCPU
public class IBMCPU : CPU
{
}
// IBM主板
public class IBMMainboard : Mainboard
{
}
// IBM内存
public class IBMMemory : Memory
{
}
// IBM硬盘
public class IBMHarddisk : Harddisk
{
}
// IBM键盘
public class IBMKeyboard : Keyboard
{
}
// IBM鼠标
public class IBMMouse : Mouse
{
}
到这里我们就需要定义,组装IBM配件的方法了。为了简单,这里又定义了一个用来存储实际需要的配件集合,用来存储我们构建出来的配件(子对象)。
public class AssemblyComputer
{
// 组装起来的电脑配件
ArrayList accessories = new ArrayList();
public void Add(string part)
{
accessories.Add(part);
}
public void ShowDetails ()
{
Console.WriteLine( "Computer Accessories: " );
foreach(string part in accessories)
Console.WriteLine(part);
}
}
这时我们的ComputerBuilder方法和集成自它的实际构建方法IBMComputerBuilder就需要有所修改,返回一个我们建立的辅助信息存储类型的对象AssemblyComputer。以下为修改后的IBM电脑子对象构建类。
public class IBMComputerBuilder : ComputerBuilder
{
AssemblyComputer ac = new AssemblyComputer();
public override void BuildCPU()
{
ac.Add("IBMCPU");
}
public override void BuildMainboard()
{
ac.Add("IBMMainboard");
}
public override void BuildMemory()
{
ac.Add("IBMMemory");
}
public override void BuildHarddisk()
{
ac.Add("IBMHarddisk");
}
public override void BuildKeyboard()
{
ac.Add("IBMKeyboard");
}
public override void BuildMouse()
{
ac.Add("IBMMouse");
}
public override AssemblyComputer GetComputer()
{
return ac;
}
}
相应的抽象类也做修改。
public abstract class ComputerBuilder
{
public abstract void BuildCPU();
public abstract void BuildMainboard();
public abstract void BuildMemory();
public abstract void BuildHarddisk();
public abstract void BuildKeyboard();
public abstract void BuildMouse();
public abstract AssemblyComputer GetComputer();
}
最后,我们定这个电脑的配置类,就是这台电脑的实际配置。
public class ComputerConfiguration
{
public static AssemblyComputer CreateComputer(ComputerBuilder computerbuilder)
{
computerbuilder.BuildCPU();
computerbuilder.BuildMainboard();
computerbuilder.BuildMemory();
computerbuilder.BuildHarddisk();
computerbuilder.BuildKeyboard();
computerbuilder.BuildMouse();
return computerbuilder.GetComputer();
}
}
其中的CreateComputer方法就是构建了一个电脑的实际配置例子。这里有一点需要强调的就是,这台电脑需要的配件是稳定的,需要CPU、主板、内存、硬盘、键盘、鼠标。至于具体需要什么牌子的,很可能客户就不要IBM的键盘鼠标,而需要一套罗技的键盘和鼠标,这时只要修改我们IBM的构建类,或者是添加一个新的自定义电脑配置类,然后在这个自定义构建类中定义我们需要的具体配件,而这些配件(子对象)构建出来的电脑对象的构建过程是不变的。
这样,我们在客户程序中使用定义好的对象时可以使用下面的代码。
AssemblyComputer ac = ComputerConfiguration.CreateComputer(new IBMComputerBuilder());
ac.ShowDetails();
Console.ReadLine();
首先定一个电脑配置,然后使用一个需要的配置的生成器,最后将电脑配置呈现出来。
得到执行结果:
Computer Accessories:
IBMCPU
IBMMainboard
IBMMemory
IBMHarddisk
IBMKeyboard
IBMMouse
看到这段代码可能大家还是感觉有新的配置后在维护的时候仍然需要修改new IBMComputerBuilder()这里,我们可以使用反射机制来解决这种问题,这样就解决了扩展上不许重新进行编译的问题。如果以前的有所修改那就直接替换掉相应的DLL程序集就可以了。方便了以后的维护扩展工作。