建造者模式

118 阅读7分钟

模式介绍

  • 《Java与模式》

建造者模式利用一个导演者对象和具体建造者对象一个一个地建造出所有的零件,从而建造出完整的产品对象。建造者模式将产品的结构与产品的零件建造过程对客户端隐藏起来,把对建造过程进行指挥的责任和具体建造者零件的责任分割开来,达到责任划分和封装的目的。

  • 尚硅谷

建造者模式又称为生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。通过一步一步创建一个复杂的对象,同时允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

模式图解

UML类图

image.png

类图说明

  • 抽象建造者(Builder)角色:给出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者(ConcreteBuilder)角色。具体建造者类必须实现这个接口所要求的两种方法:一种是建造方法,另一种是结果返还方法。一般来说,产品所包含的零件数目与建造方法的数目相符。换言之,有多少零件,就有多少相应的建造方法。

  • 具体建造者(ConcreteBuilder)角色:担任这个角色的是与应用程序紧急相关的一些类,它们在应用程序调用下创建产品的实例。这个角色要完成的任务包括:

    1. 实现抽象建造者 Builder 所声明的接口,给出一步一步地完成创建产品实例的操作。
    2. 在建造过程完成后,提供产品的实例。
  • 导演者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象。应当指出的是,导演者角色并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者角色。

  • 产品(Product)角色:产品(Product)便是建造中的复杂对象。一般来说,一个系统中会有多于一个的产品类,而且这些产品类并不一定有共同的接口,而完全可以是不相关联的。

导演者角色是与客户端打交道的角色。导演者角色将客户端创建产品的请求划分为对各个零件的建造请求,再将这些请求委派给具体建造者角色。具体建造者角色是做具体建造工作的,但是却不为客户端所知。

一般来说,每有一个产品类,就有一个相应的具体建造者类。这些产品应当有一样数目的零件,而每有一个零件就相应地在所有的建造者角色里有一个建造方法。

示例代码

建造者模式--指挥者角色

public class BuildDirector {

    private HouseBuilder houseBuilder;

    public void setHouseBuilder(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    public House build() {
        houseBuilder.buildFoundation();
        houseBuilder.buildWall();
        houseBuilder.buildRoof();
        return houseBuilder.buildHouse();
    }
}

建造者模式--抽象建造者角色

public abstract class HouseBuilder {

    private House house = new House();

    public abstract void buildFoundation();

    public abstract void buildWall();

    public abstract void buildRoof();

    public House buildHouse() {
        return house;
    }
}

建造者模式--具体建造者角色

public class CommonHouse extends HouseBuilder {

    @Override
    public void buildFoundation() {
        System.out.println(" 普通房子打地基 10米 ");
    }

    @Override
    public void buildWall() {
        System.out.println(" 普通房子砌墙 10厘米 ");
    }

    @Override
    public void buildRoof() {
        System.out.println(" 普通房子屋顶 ");
    }
}
public class LargeBuilding extends HouseBuilder {

    @Override
    public void buildFoundation() {
        System.out.println(" 大厦打地基 100米 ");
    }

    @Override
    public void buildWall() {
        System.out.println(" 大厦砌墙 10米 ");
    }

    @Override
    public void buildRoof() {
        System.out.println(" 大厦屋顶 ");
    }
}

建造者模式--产品角色

public class House {

    private String foundation;
    private String wall;
    private String roof;

    //... get/set
}

客户端测试代码

public class Client {
    public static void main(String[] args) {
        BuildDirector buildDirector = new BuildDirector();

        HouseBuilder houseBuilder = new ConcreteHouse();
        buildDirector.setHouseBuilder(houseBuilder);
        buildDirector.build();

        houseBuilder = new LargeBuilding();
        buildDirector.setHouseBuilder(houseBuilder);
        buildDirector.build();
    }
}

使用建造模式的情况

  1. 需要生成的产品对象有复杂的内部结构。每一个内部成分本身可以是对象,也可以仅仅是一个对象(即产品对象)的一个组成成分。

  2. 需要生成的产品对象的属性相互依赖。建造模式可以强制实行一种分步骤进行的建造过程,因此,如果产品对象的一个属性必须在另一个属性被赋值之后才可以被赋值,使用建造模式便是一个很好的设计思想。

与其它模式的关联

模版方法模式

准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模版方法模式。

经过特殊退化的建造模式与模版方法模式有相似之处:HouseBuilder() 方法就相当于模版方法,这个方法调用其它的建造方法,如 buildFoundation()、buildWall()、buildRoof() 等基本方法,因此,这使得此系统与模版方法模式相同。

这就是说,如果系统的要求发生变化,要求有不同的零件生成逻辑时,那么设计师就有两种选择:一是修改这个退化的建造模式,将它改回成为完全的建造模式,这当然涉及到代码的修改;二是不修改已有的代码,而是将 Builder 类扩展到不同的子类,在这些子类里面置换掉需要改变的建造方法。

第一种选择局限于建造模式,第二种选择则引入了模版方法模式。

抽象工厂

抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件来产生一个新产品。

注意事项

  • 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象;

  • 每一个具体建造者都相对独立,而与其它的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象;

  • 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程;

  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围收到一定的限制;

  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者来实现这种变化,导致系统变得很庞大,因此在这种情况下,需要考虑是否选择使用建造者模式。