设计模式-(6)建造者模式(Builder Pattern)

一、引言
  在软件系统中,有时需要创建一个复杂对象,并且这个复杂对象由其各部分子对象通过一定的步骤组合而成。例如一个采购系统中,如果需要采购员去采购一批电脑时,在这个实际需求中,电脑就是一个复杂的对象,它是由CPU、主板、硬盘、显卡、机箱等组装而成的,如果此时让采购员一台一台电脑去组装的话真是要累死采购员了,这里就可以采用建造者模式来解决这个问题,我们可以把电脑的各个组件的组装过程封装到一个建造者类对象里,建造者只要负责返还给客户端全部组件都建造完毕的产品对象就可以了。然而现实生活中也是如此的,如果公司要采购一批电脑,此时采购员不可能自己去买各个组件并把它们组织起来,此时采购员只需要像电脑城的老板说自己要采购什么样的电脑就可以了,电脑城老板自然会把组装好的电脑送到公司。下面就以这个例子来展开建造者模式的介绍。

二、建造者模式的详细介绍

  在这个例子中,电脑城的老板是直接与客户(也就是指采购员)联系的,然而电脑的组装是由老板指挥装机人员去把电脑的各个部件组装起来,真真负责创建产品(这里产品指的就是电脑)的人就是电脑城的装机人员。理清了这个逻辑过程之后,下面就具体看下如何用代码来表示这种现实生活中的逻辑过程:

/// <summary>
/// 电脑类
/// </summary>
public class Computer
{
    // 电脑组件集合
    private IList<string> parts = new List<string>();

    // 把单个组件添加到电脑组件集合中
    public void Add(string part)
    {
        parts.Add(part);
    }

    public void Show()
    {
        Console.WriteLine("电脑开始在组装.......");
        foreach (string part in parts)
        {
            Console.WriteLine("组件" + part + "已装好");
        }

        Console.WriteLine("电脑组装好了");
    }
}

/// <summary>
/// 小王和小李难道会自愿地去组装嘛,谁不想休息的,这必须有一个人叫他们去组装才会去的
/// 这个人当然就是老板了,也就是建造者模式中的指挥者
/// 指挥创建过程类
/// </summary>
public class Director
{
    // 组装电脑
    public void Construct(Builder builder)
    {
        builder.BuildPartCPU();
        builder.BuildPartMainBoard();
    }
}

/// <summary>
/// 抽象建造者,这个场景下为 "组装人" ,这里也可以定义为接口
/// </summary>
public abstract class Builder
{
    // 装CPU
    public abstract void BuildPartCPU();
    // 装主板
    public abstract void BuildPartMainBoard();

    // 当然还有装硬盘,电源等组件,这里省略

    // 获得组装好的电脑
    public abstract Computer GetComputer();
}

/// <summary>
/// 具体创建者,具体的某个人为具体创建者,例如:装机小王啊
/// </summary>
public class ConcreteBuilder1 : Builder
{
    Computer computer = new Computer();
    public override void BuildPartCPU()
    {
        computer.Add("CPU1");
    }

    public override void BuildPartMainBoard()
    {
        computer.Add("Main board1");
    }

    public override Computer GetComputer()
    {
        return computer;
    }
}

/// <summary>
/// 具体创建者,具体的某个人为具体创建者,例如:装机小李啊
/// 又装另一台电脑了
/// </summary>
public class ConcreteBuilder2 : Builder
{
    Computer computer = new Computer();
    public override void BuildPartCPU()
    {
        computer.Add("CPU2");
    }

    public override void BuildPartMainBoard()
    {
        computer.Add("Main board2");
    }

    public override Computer GetComputer()
    {
        return computer;
    }
}

/// <summary>
/// 客户类
/// </summary>
class Customer
{
    static void Main(string[] args)
    {
        // 客户找到电脑城老板说要买电脑,这里要装两台电脑
        // 创建指挥者和构造者
        Director director = new Director();
        Builder b1 = new ConcreteBuilder1();
        Builder b2 = new ConcreteBuilder2();

        // 老板叫员工去组装第一台电脑
        director.Construct(b1);

        // 组装完,组装人员搬来组装好的电脑
        Computer computer1 = b1.GetComputer();
        computer1.Show();

        // 老板叫员工去组装第二台电脑
        director.Construct(b2);
        Computer computer2 = b2.GetComputer();
        computer2.Show();

        Console.Read();
    }
}

介绍完了建造者模式的具体实现之后,下面具体看下建造者模式的具体定义是怎样的。
定义:
建造者模式:将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式使得建造代码与表示代码的分离,可以使客户端不必知道产品内部组成的细节,从而降低了客户端与具体产品之间的耦合度。

实用范围
1)、当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
2)、当构造过程必须允许被构造的对象有不同表示时。

角色
在这样的设计模式中,有以下几个角色:
1)Builder(抽象建造者):它为创建一个产品对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是BuildXXX(),它们用于创建复杂对象的各个部件;另一类方法是GetXXX(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。
2)ConcreteBuilder(具体建造者):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
3)Director (指挥者):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象,然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。
4)Product (产品角色):表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

什么是复杂对象:是指那些包含多个成员属性的对象,这些成员属性也称为部件或零件,如程序猿要会识字、会数学、会编程语言,会设计模式等等。

建造者模式在使用过程中可以演化出多种形式:
如果具体的被建造对象只有一个的话,可以省略抽象的Builder和Director,让ConcreteBuilder自己扮演指导者和建造者双重角色,甚至ConcreteBuilder也可以放到Product里面实现。

/// <summary>
/// 产品类
/// </summary>
public class Product
{
    // 产品组件集合
    private IList<string> parts = new List<string>();

    // 把单个组件添加到产品组件集合中
    public void Add(string part)
    {
        parts.Add(part);
    }

    public void Show()
    {
        Console.WriteLine("产品开始在组装.......");
        foreach (string part in parts)
        {
            Console.WriteLine("组件" + part + "已装好");
        }

        Console.WriteLine("产品组装完成");
    }
}


/// <summary>
/// 建造者模式的演变
/// 省略了指挥者角色和抽象建造者角色
/// 此时具体建造者角色扮演了指挥者和建造者两个角色
/// </summary>
public class Builder
{
    // 具体建造者角色的代码
    private Product product = new Product();
    public void BuildPartA()
    {
        product.Add("PartA");
    }
    public void BuildPartB()
    {
        product.Add("PartB");
    }
    public Product GetProduct()
    {
        return product;
    }
    // 指挥者角色的代码
    public void Construct()
    {
        BuildPartA();
        BuildPartB();
    }
}


// 此时客户端也要做相应调整
class Client 
{
    private static Builder builder;
    static void Main(string[] args)
    {
        builder = new Builder();
        builder.Construct();
        Product product = builder.GetProduct();
        product.Show();
        Console.Read();
    }
}

三、总结

介绍完了建造者模式的具体实现之后,让我们总结下建造模式的实现要点
1)在建造者模式中,指挥者是直接与客户端打交道的,指挥者将客户端创建产品的请求划分为对各个部件的建造请求,再将这些请求委派到具体建造者角色,具体建造者角色是完成具体产品的构建工作的,却不为客户所知道。
2)建造者模式主要用于“分步骤来构建一个复杂的对象”,其中“分步骤”是一个固定的组合过程,而复杂对象的各个部分是经常变化的(也就是说电脑的内部组件是经常变化的,这里指的的变化如硬盘的大小变了,CPU由单核变双核等)。
3)产品不需要抽象类,由于建造模式的创建出来的最终产品可能差异很大,所以不大可能提炼出一个抽象产品类。
4)在前面文章中介绍的抽象工厂模式解决了“系列产品”的需求变化,而建造者模式解决的是 “产品部分” 的需要变化
5)由于建造者隐藏了具体产品的组装过程,所以要改变一个产品的内部表示,只需要再实现一个具体的建造者就可以了,从而能很好地应对产品组成组件的需求变化。

建造者模式优点:
1)建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
2)每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合“开闭原则”。
3)可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。

建造者模式缺点:
1)建造者模式所创建的产品具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,不适合使用建造者模式,因此其使用范围受到一定的限制。
2)如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。

  到这里,建造者模式的介绍就结束了,建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使的同样的构建过程可以创建不同的表示建造者模式的本质使组装过程(用指挥者类进行封装,从而达到解耦的目的)和创建具体产品解耦,使我们不用去关心每个组件是如何组装的

猜你喜欢

转载自blog.csdn.net/num197/article/details/80293085