likes
comments
collection
share

【设计模式】一文速通建造者模式小九开了一家汉堡店,客人可以根据自己的口味定制汉堡,汉堡有多种配料可选: 面包:全麦面包,

作者站长头像
站长
· 阅读数 20

1. 引入

小九开了一家汉堡店,客人可以根据自己的口味定制汉堡,汉堡有多种配料可选:

  • 面包:全麦面包,白面包...
  • 肉饼:鸡肉,牛肉...
  • 蔬菜:生菜,酸黄瓜,番茄...
  • 酱料:番茄酱,蜂蜜芥末酱...

不同客人的口味不同,如何根据客人需要去创建客人想要的汉堡呢?以下是两种实现方案。

1.1 方案一:构造方法实现

1.1.1 实现代码

  • 定义实体类Burger,实体类中定义构造方法
@Data
public class Burger {
    // 面包
    private String bread;
    // 肉饼
    private String patty;
    // 蔬菜
    private List<String> vegetables;
    // 酱料
    private List<String> sauces;

    // 汉堡的构造方法
    public Burger(String bread, String patty, List<String> vegetables, List<String> sauces) {
        this.bread = bread;
        this.patty = patty;
        this.vegetables = vegetables;
        this.sauces = sauces;
    }

    @Override
    public String toString() {
        return "Burger{" +
               "bread='" + bread + '\'' +
               ", patty='" + patty + '\'' +
               ", vegetables=" + vegetables +
               ", sauces=" + sauces +
               '}';
    }
}
  • 根据客人需求创建Burger对象
public static void main(String[] args) {
    // 客人选择的蔬菜
    List<String> vegetables = Arrays.asList("Lettuce", "Tomato");
    // 客人选择的酱料
    List<String> sauces = Arrays.asList("Ketchup");
    // 通过构造方法创建burger
    Burger burger = new Burger("Sesame", "Beef", vegetables, sauces);
    System.out.println(burger);
}

1.1.2 上述方案存在问题

  • 构造函数参数多:构造函数参数多且顺序复杂,容易出错;如果将来需要添加更多属性,构造函数的参数列表会很多且难以管理;
  • 可读性差:创建对象时很难知道每个参数的意义,尤其是当参数多的时候;
  • 灵活性差:难以扩展和修改对象的构造方式,一旦增加新属性,需要修改构造函数和相关代码。

1.2 方案二:采用建造者模式实现

使用建造者模式相当于定义了一个汉堡定做器,通过汉堡定做器可以一步步指定每种配料,最后得到一个完全符合客户要求的汉堡。

  • 定义Builder接口,定义构建汉堡的步骤。
public interface Builder {
    Builder setBread(String bread);
    Builder setPatty(String patty);
    Builder addVegetable(String vegetable);
    Builder addSauce(String sauce);
    Burger build();
}

  • 具体的建造者,实现Builder接口,提供了如何构建一个具体的汉堡。
public class BurgerBuilder implements Builder {
    private String bread;
    private String patty;
    private List<String> vegetables = new ArrayList<>();
    private List<String> sauces = new ArrayList<>();

    @Override
    public Builder setBread(String bread) {
        this.bread = bread;
        return this;
    }

    @Override
    public Builder setPatty(String patty) {
        this.patty = patty;
        return this;
    }

    @Override
    public Builder addVegetable(String vegetable) {
        this.vegetables.add(vegetable);
        return this;
    }

    @Override
    public Builder addSauce(String sauce) {
        this.sauces.add(sauce);
        return this;
    }

    @Override
    public Burger build() {
        return new Burger(new Burger.Builder()
                              .setBread(this.bread)
                              .setPatty(this.patty)
                              .addVegetable(this.vegetables)
                              .addSauce(this.sauces));
    }
}

  • 定义Burger实体类,表示最终创建的汉堡。
public class Burger {
    private String bread;
    private String patty;
    private List<String> vegetables;
    private List<String> sauces;

    private Burger(Builder builder) {
        this.bread = builder.bread;
        this.patty = builder.patty;
        this.vegetables = builder.vegetables;
        this.sauces = builder.sauces;
    }

    @Override
    public String toString() {
        return "Burger{" +
               "bread='" + bread + '\'' +
               ", patty='" + patty + '\'' +
               ", vegetables=" + vegetables +
               ", sauces=" + sauces +
               '}';
    }

    public static class Builder {
        private String bread;
        private String patty;
        private List<String> vegetables = new ArrayList<>();
        private List<String> sauces = new ArrayList<>();

        public Builder setBread(String bread) {
            this.bread = bread;
            return this;
        }

        public Builder setPatty(String patty) {
            this.patty = patty;
            return this;
        }

        public Builder addVegetable(String vegetable) {
            this.vegetables.add(vegetable);
            return this;
        }

        public Builder addSauce(String sauce) {
            this.sauces.add(sauce);
            return this;
        }

        public Burger build() {
            return new Burger(this);
        }
    }
}

  • Director指挥者,定义了调用构造步骤的顺序,封装了建造的过程,确保汉堡各个部分按正确的顺序创建。
public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public Burger construct() {
        return builder.setBread("Sesame")
                      .setPatty("Beef")
                      .addVegetable("Lettuce")
                      .addVegetable("Tomato")
                      .addSauce("Ketchup")
                      .build();
    }
}

public static void main(String[] args) {
    Builder builder = new BurgerBuilder();
    Director director = new Director(builder);
    Burger burger = director.construct();
    System.out.println(burger);
}
【设计模式】一文速通建造者模式小九开了一家汉堡店,客人可以根据自己的口味定制汉堡,汉堡有多种配料可选: 面包:全麦面包,

1.2.2 上述方案优点

  • 清晰的API:通过链式调用,创建对象时更清晰、可读。
  • 灵活性:可以选择性地设置属性,构建复杂对象时更灵活。
  • 可维护性:新增属性时,不需要修改现有的构造函数,可以在建造者中添加新的设置方法。
  • 简化代码:减少了参数列表的复杂性,使代码更易于管理和理解。

2. 建造者模式

2.1 定义

建造者模式/生成器模式/Builder:用来创建一个对象,将一个复杂对象的创建与其表示分离,使用者不需要关心对象创建的流程,只需要知道一个接口就可以一步步构造一个比较复杂的对象。

可以理解为工人将许许多多的零部件拼凑在一起,搭建成一个完整的产品

【设计模式】一文速通建造者模式小九开了一家汉堡店,客人可以根据自己的口味定制汉堡,汉堡有多种配料可选: 面包:全麦面包,

2.2 角色及结构

2.2.1 角色

  • Product产品类:要创建的目标对象,如:汉堡。

  • Builder抽象建造者:为创建一个Product对象的各个部分指定的抽象接口。如:添加蔬菜,酱料等的方法。

  • ConcreteBuilder具体建造者:实现 Builder接口,构造和装配各个部件。如:具体的汉堡,具体实现如何构造出汉堡的各个部分。

  • Director指挥者类:构建一个使用Builder接口的对象。如:根据用户的需求创建想要的汉堡。

2.2.2 结构

【设计模式】一文速通建造者模式小九开了一家汉堡店,客人可以根据自己的口味定制汉堡,汉堡有多种配料可选: 面包:全麦面包,

2.3 适用场景

用于创建复杂对象,且对象内部构造的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化,可以理解为:构造过程一样,但是每个过程的实现细节不一样

【例】 如:画一个小人,画一个小人的步骤可以概括为:

  • 画头
  • 画身体
  • 画手
  • 画脚

但是画胖小人和瘦小人的实现细节就不一样了。

对于用户来说,不想关注实现细节。只需要指定需要构造的类型即可,无需自己设置参数。

如:用户指定构建一个胖小人,直接指定胖,那么直接生成一个胖小人,无需关注身体、手、脚等的参数。

2.4 优缺点

2.4.1 优点

  • 符合单一职责原则:隔离复杂对象的创建和使用,客户端无需关心对象创建细节。

  • 更加灵活:可以分步创建对象,构建过程更加灵活。

  • 符合开闭原则:增加新的具体Builder很方便,可以扩展Builder功能。

2.4.2 缺点

代码复杂度会增加。

参考资料

转载自:https://juejin.cn/post/7408391774635950132
评论
请登录