设计模式(4) 建造者模式

  • 什么是建造者模式
  • 经典建造者模式的优缺点
  • 对建造者模式的扩展

什么是建造者模式

建造者模式将一个复杂的对象的构建与它的表示分离,使得一样的构建过程能够建立不一样的表示。建立者模式隐藏了复杂对象的建立过程,它把复杂对象的建立过程加以抽象,经过子类继承或者重载的方式,动态的建立具备复合属性的对象。
虽然与工厂模式、抽象工厂模式、单件模式同为建立型模式,但建造者模式与以前学习的模式相比,更为关注建立过程的细节,它通常用于建立复杂对象,从独立建立每一个部分到最后的组装,它承担每一个步骤的工做。因为它把建立每一个部分都独立为一个单一的过程,所以不只能够完成较为精细的建立,还能够根据建立步骤编排,生成不一样的目标实例。
GOF对建造者模式的描述是:
Separate the construction of a complex object from its representation so that the same construction process can create different representations..
— Design Patterns : Elements of Reusable Object-Oriented Software设计模式

建立者模式很是适用于产品局部加工过程变化较大,但组装过程相对固定的场景。
好比电脑的组装,基本的组装过程是固定的,可是具体主板、CPU、显卡、内存、硬盘等选择的品牌和型号可能差别很大;还有汽车的生产也是这样,总体组装过程基本相同,但不一样品牌、价格的汽车在具体部件上差别很大。markdown

其UML类图以下:
markdown学习

其中包括三个角色:优化

  • IBuilder:负责描述建立一个产品各个组成的抽象接口。
  • Concrete Builder:实现IBuilder要求的内容,而且提供一个得到产品的方法。
  • Director:基于IBuilder定义的构造产品的抽象步骤,指导Concrete Builder生成产品的过程。

这里的产品类型并无统一的IProduct接口,主要是由于通过不一样ConcreteBuilder加工后的产品差异相对较大,给它一个公共的基准抽象对象意义不大,ui

代码实现:设计

public class House
{
    public void AddWindowAndDoor() { }
    public void AddWallAndFloor() { }
    public void AddCeiling() { }
}

public class Car
{
    public void AddWheel() { }
    public void AddEngine() { }
    public void AddBody() { }
}

public interface IBuilder
{
    void BuildPart1();
    void BuildPart2();
    void BuildPart3();
}

public class CarBuilder : IBuilder
{
    private Car car;
    public void BuildPart1()
    {
        car.AddEngine();
    }
    public void BuildPart2()
    {
        car.AddWheel();
    }
    public void BuildPart3()
    {
        car.AddBody();
    }
}

public class HouseBuilder : IBuilder
{
    private House house;
    public void BuildPart1()
    {
        house.AddWallAndFloor();
    }
    public void BuildPart2()
    {
        house.AddCeiling();
    }
    public void BuildPart3()
    {
        house.AddWindowAndDoor();
    }
}

public class Director
{
    public void Construct(IBuilder builder)
    {
        builder.BuildPart1();
        builder.BuildPart2();
        builder.BuildPart3();
    }
}

调用:3d

[Test]
public void BuilderTest()
{
    Director director = new Director();
    CarBuilder carBuilder = new CarBuilder();
    HouseBuilder houseBuilder = new HouseBuilder();

    director.Construct(carBuilder);
    director.Construct(houseBuilder);

    Assert.AreEqual(typeof(Car), carBuilder.Car.GetType());
    Assert.AreEqual(typeof(House), houseBuilder.House.GetType());
}

经典建造者模式的优缺点

  • 优势:建立者模式将复杂对象的每一个组成建立步骤暴露出来,借助Director(或客户程序本身)既能够选择其执行次序,也能够选择要执行哪些步骤。上述过程能够在应用中动态完成,相比较工厂方法和抽象工厂模式的一次性建立过程而言,建立者模式适合建立“更为复杂且每一个组成变化较多”的类型。
  • 缺点:但建造者模式也存在一些缺点,好比正由于会暴露出更多的执行步骤,这就须要须要Director(或客户程序)具备更多的领域知识,使用不慎很容易形成相对更为紧密的耦合。

并且经典建造者中IBuilder定义了数目固定的装配动做,而Director有把这些动做的执行顺序也固定了,虽然建造者模式能够生产差别很是大的产品,但要求这些产品具备固定的装配步骤,这就大大局限了这种模式的使用场景,由于现实中这样的要求每每很难知足。不一样的产品每每具备数目不一样的装配动做和次序,若是要把这样的产品添加到建造者的生产列表中是作不到的,须要另外实现一套,改动比较大。code

对建造者模式的扩展

上述问题能够经过对经典模式适当优化来解决。
IBuilder中定义的不一样步骤能够进一步抽象为一个Action,CarBuilder和HouseBuilder的代码也很类似,能够提取一个基类,这部分代码放在基类中。
代码以下:对象

public interface IBuilder<T> where T : class, new()
{
    T BuildUp();
}

public abstract class BuilderBase<T> : IBuilder<T> where T : class, new()
{
    protected IList<Action> steps = new List<Action>();

    protected T product = new T();
    public virtual T BuildUp()
    {
        foreach (Action step in steps)
        {
            step();
        }
        return product;
    }
}

public class ConcreteCarBuilder : BuilderBase<Car>
{
    public ConcreteCarBuilder() : base()
    {
        steps.Add(product.AddEngine);
        steps.Add(product.AddWheel);
        steps.Add(product.AddBody);
    }
}

public class ConcreteHouseBuilder : BuilderBase<House>
{
    public ConcreteHouseBuilder() : base()
    {
        steps.Add(product.AddWallAndFloor);
        steps.Add(product.AddCeiling);
        steps.Add(product.AddWindowAndDoor);
    }
}

实体Builder兼作Director,具体的构建步骤由实体Builder来决定,这样作的好处是很是灵活,不一样的Builder能够有不一样数目的动做,动做的顺序也能够自行安排。blog

调用:

[Test]
public void DelegateBuilderTest()
{
    IBuilder<Car> builder = new ConcreteCarBuilder();
    var product = builder.BuildUp();
    Assert.AreEqual(typeof(Car), product.GetType());

    IBuilder<House> builder1 = new ConcreteHouseBuilder();
    var product1 = builder1.BuildUp();
    Assert.AreEqual(typeof(House), product1.GetType());
}

参考书籍: 王翔著 《设计模式——基于C#的工程化实现及扩展》

相关文章
相关标签/搜索