一、Builder模式的缘起(也就是需求)
1、假设创建游戏中的一个房屋House设施,该房屋的构建由几个部分组成,且各个部分要富于变化。
2、如果使用最直观的设计方法,每一个房屋部分的变化,都将导致房屋构建的重新修正......
二、动机
在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成,由于需要的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
如何应对这种变化,如何提供一种“封装机制”来隔离出“复杂对象各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需要的变化而变化。
三、类图
四、代码
1、Build.cs
namespace Builder
{
public abstract class House
{
}
public abstract class Door
{
}
public abstract class Wall
{
}
public abstract class Windows
{
}
public abstract class Floor
{
}
public abstract class HouseCelling
{
}
public abstract class Builder
{
public abstract void BuildDoor();
public abstract void BuildWall();
public abstract void BuildWindows();
public abstract void BuildFloor();
public abstract void BuildHouseCelling();
public abstract House GetHouse();
}
}
2、RomaHouse.cs
namespace Builder
{
public class RomaHouse:House
{
public RomaHouse()
{
Console.WriteLine("/n建设罗马式房屋");
}
}
public class RomaDoor : Door
{
public RomaDoor()
{
Console.WriteLine("/n建设罗马式门");
}
}
public class RomaWall : Wall
{
public RomaWall()
{
Console.WriteLine("/n建设罗马式墙");
}
}
public class RomaWindows : Windows
{
public RomaWindows()
{
Console.WriteLine("/n建设罗马式窗");
}
}
public class RomaFloor : Floor
{
public RomaFloor()
{
Console.WriteLine("/n建设罗马式地板");
}
}
public class RomaHouseCelling : HouseCelling
{
public RomaHouseCelling()
{
Console.WriteLine("/n建设罗马式屋顶");
}
}
public class RomaHouseBuilder : Builder
{
public override void BuildDoor()
{
Door door = new RomaDoor();
Console.WriteLine("/n罗马式门建成了");
}
public override void BuildWall()
{
Wall wall = new RomaWall();
Console.WriteLine("/n罗马式墙建成了");
}
public override void BuildWindows()
{
Windows windows = new RomaWindows();
Console.WriteLine("/n罗马式窗建成了");
}
public override void BuildFloor()
{
Floor floor = new RomaFloor();
Console.WriteLine("/n罗马式地板建成了");
}
public override void BuildHouseCelling()
{
HouseCelling housecelling = new RomaHouseCelling();
Console.WriteLine("/n罗马式屋顶建成了");
}
public override House GetHouse()
{
return new RomaHouse();
}
}
}
3、GameManager.cs
namespace Builder
{
public class GameManager
{
public static House CreateHouse(Builder builder)
{
builder.BuildDoor();
builder.BuildWall();
builder.BuildWindows();
builder.BuildFloor();
builder.BuildHouseCelling();
return builder.GetHouse();
}
}
}
4、Client.cs
namespace Builder
{
class App
{
public static void Main()
{
string builderName = ConfigurationSettings.AppSettings["BuildName"].ToString();
string assemblyName = ConfigurationSettings.AppSettings["BuildAssembly"].ToString();
Assembly assembly = Assembly.Load(assemblyName);
Type t = assembly.GetType(builderName);
Builder builder = (Builder)Activator.CreateInstance(t);
House house = GameManager.CreateHouse(builder);
Console.ReadLine();
}
}
}
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="BuildName" value="Builder.RomaHouseBuilder"></add>
<add key="BuildAssembly" value="Builder"></add>
</appSettings>
</configuration>
五、代码分析
首先看看运行结果:
大家看代码之后也许会认为,生成器模式与抽象工厂模式几乎没什么区别,没错,确实很相似,现在我来谈谈不同之处。
首先我们来从代码谈起,大家注意两种模式最大的不同就是GameManager.cs这个类,生成器模式中的GameManager类中各个子对象都互不相干,由各个子对象来生成一个大的对象;而抽象工厂模式GameManager类中各个对象是耦合的,大家注意到Building类中building(Road road)方法没有(也许游戏场景中房屋下面有路)
下面总结一下两种模式是不同:
生成器模式解决“对象部分”的需求变化,如例中假设房屋里面加上一个床,那将是相当方便的,只需要新增一个床类,然后在GameManager中加上builder.BuildBed()就ok了。
抽象工厂模式解决是“系列对象”的需求变化,如例中只有在道路,房屋,地道,丛林这四种均不会变化的情况下才适用,假设再加一个沙漠或者是海洋什么的,这种模式就不适用了。
最后说明一点,在App.config中有两个地方可由用户配置程序集和类名,main方法中利用反射机制根据程序集和类名实例化一个类对象,这样就可以很方便地修改房屋的风格(比如当前是Roma风格的,您也可以新增一个China风格的)