欢迎光临
我们一直在努力

【从0开始学设计模式-5| 建造者模式】

在这里插入图片描述

建造者模式还是比较简单的!

概述

定义如下:

建造者模式是一种对象创建软件设计模式,旨在找到伸缩构造器反模式的解决方案。

伸缩性构造的反模式:指通过构造器实现对象构建参数初始化,如果对象属性比较多,导致构造器的参数个数不可控。

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

结构

建造者模式一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
在这里插入图片描述

图中包含如下几个角色:

  • Product(产品角色):被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。

  • Builder(抽象建造者):它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildPartXXXX(),它们用于创建复杂对象的各个部件;另一类方法是build(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。

  • ConcreteBuilder(具体建造者):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。

  • Director(指挥者):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。

    客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。

简单说就是:

  • 如果用户通过Director创建对象,创建过程是由Director的construct()方法规定的,且是规定死的,一般不做修改。用户可以通过配置文件(或者反射)选择不同的ConcreteBuilder作为Builder(具体创建者),进而通过多态传参进入construct()方法,然后按照不同子类的不同buildPartXXXX()进行构造,只不过构建过程是一样的,而创建出的产品不同(例子见下面的实现1)
  • 有时候为了提高构建灵活性,提供更丰富的组合,可以不使用Director,而是把Builder写到复杂对象里面,让用户自己指定调用初始化的顺序以及选择使用哪些方法初始化(例子见下面的实现2)

实现1

类图设计

在这里插入图片描述

代码实现

  • 复杂的产品类(为了演示字段定义的不多,能明白意思即可)

    @Getter
    @Setter
    @ToString
    public class Human {
    private String type;
    private String region;
    }

  • 抽象建造者以及具体实现类

    public abstract class HumanBuilder {
    protected Human human = new Human();
    public abstract HumanBuilder buildType();
    public abstract HumanBuilder buildRegion();
    public Human build() {
    //对象复制
    Human target = new Human();
    target.setType(human.getType());
    target.setRegion(human.getRegion());
    return target;
    }
    }

    public class YellowManBuilder extends HumanBuilder {
    @Override
    public HumanBuilder buildType() {
    human.setType("黄种人");
    return this;
    }

    @Override
    public HumanBuilder buildRegion() {
    human.setRegion("华夏");
    return this;
    }
    }

    public class WhiteManBuilder extends HumanBuilder {
    @Override
    public HumanBuilder buildType() {
    human.setType("白种人");
    return this;
    }

    @Override
    public HumanBuilder buildRegion() {
    human.setRegion("欧洲");
    return this;
    }
    }

    public class BlackManBuilder extends HumanBuilder {
    @Override
    public HumanBuilder buildType() {
    //继承了父类创建的具体Human对象
    human.setType("黑人");
    return this;//返回当前对象
    }

    @Override
    public HumanBuilder buildRegion() {
    human.setRegion("非洲");
    return this;//返回当前对象
    }
    }

  • 指挥者

    @AllArgsConstructor
    public class HumanDirector {
    private HumanBuilder builder;
    public Human construct(){
    return builder
    .buildType()
    .buildRegion()
    .build();
    }
    }

  • 配置类

    bp1=com.zeroone.star.builder.ex1.builder.YellowManBuilder

  • 客户端

    public class App {
    public static void main(String[] args) throws Exception {
    //加载配置
    ResourceBundle conf = ResourceBundle.getBundle("conf");
    String bp1 = conf.getString("bp1");
    //构建Builder
    HumanBuilder builder = (HumanBuilder) Class.forName(bp1).newInstance();
    //创建指挥者
    HumanDirector director = new HumanDirector(builder);
    //创建人类
    Human human = director.construct();
    System.out.println(human);
    }
    }

解释实现流程:

  • 通过配置文件+反射实现了动态加载,构建出具体的Builder(符合开闭原则)。
  • 根据这个Builder实例利用Director的构造函数创建Director
  • 之后调用Director的construct方法。construct方法里面的具体实现是由刚刚传入的Builder实例具体执行的(具体的Builder决定对象的具体内容)

实现2

类图设计

在这里插入图片描述

代码实现

  • 复杂对象类(创建者使用静态内部类Builder)

    @Getter
    @ToString
    public class Hero {
    private String name;
    private Profession profession;
    private Armor armor;
    private HairColor hairColor;
    private HairType hairType;
    private Weapon weapon;
    public static class Builder {
    private String name;
    private Profession profession;
    private Armor armor;
    private HairColor hairColor;
    private HairType hairType;
    private Weapon weapon;
    //构造函数
    public Builder(String name, Profession profession) {
    this.name = name;
    this.profession = profession;
    }
    //具体方法
    public Builder withArmor(Armor armor){
    this.armor = armor;
    return this;//返回当前对象
    }
    public Builder withHairColor(HairColor hairColor){
    this.hairColor = hairColor;
    return this;
    }
    public Builder withHairType(HairType hairType){
    this.hairType = hairType;
    return this;
    }
    public Builder withWeapon(Weapon weapon){
    this.weapon = weapon;
    return this;
    }
    //build方法
    public Hero build(){
    return new Hero(this);
    }
    }
    private Hero(Builder builder) {
    this.name = builder.name;
    this.profession = builder.profession;
    this.armor = builder.armor;
    this.hairColor = builder.hairColor;
    this.hairType = builder.hairType;
    this.weapon = builder.weapon;
    }
    }

  • Profession、HairColor这些内部引用对象的实现省略。。。都写成下面的样子(都是空实现):

    public class Armor {
    }
    public class HairColor {
    }
    ......

  • 客户端

    public class App {
    public static void main(String[] args) {
    //类名.内部类名的方式“实例化”建造者
    Hero hero = new Hero.Builder("阿狸", new Profession())//初始化建造者:执行了定义的Builder(String name, Profession profession) 构造函数。
    .withArmor(new Armor())//"安装零件"
    .withWeapon(new Weapon())
    .build();//最后调用创建者的build方法,把整个Builder对象作为参数,传递给了 Hero 的私有构造函数。做了属性拷贝
    System.out.println(hero);
    }
    }

总结一下:

  • 实现1这种方式,很适合:相同的步骤,产生不同的具体产品。适用于"标准化"生产

  • 而相比于实现1,实现2的方式更灵活,没有外部的 Director(指挥者),调用者可以任意组合,选任意的属性个数或者打乱顺序都可以。适用于按需配置对象。

使用场景

建造者模式的核心在于如何一步步构建一个包含多个组成部件的完整对象,使用相同的构建过程构建不同的产品,在软件开发中,如果我们需要创建复杂对象并希望系统具备很好的灵活性和可扩展性可以考虑使用建造者模式。

主要优点

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

主要缺点

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

适用环境

在以下情况下可以考虑使用建造者模式:

  • 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
  • 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
  • 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

补充

典型案例:Lombok的Build注解就使用了创建者模式(类似于实现2),例子如下:

@Builder // 自动生成 Builder 模式的所有代码
@ToString
public class Human {
private String type;
private String region;
}

public class App {
public static void main(String[] args) {
//随心所欲的组装
Human blackMan = Human.builder()
.region("非洲")
.type("黑人")
.build();

Human whiteMan = Human.builder()
.type("白人")
.region("欧洲")
.build();

System.out.println(blackMan);
}
}

相比于工厂模式,工厂模式关注的是创建出的东西,而建造者模式关注的是创建的过程!

如果这篇文章对你有帮助,欢迎点赞、评论、关注、收藏。你们的支持是我前进的动力!

赞(0)
未经允许不得转载:171主机测评 » 【从0开始学设计模式-5| 建造者模式】
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址