Java设计模式 建造者模式

前言

建造者模式(Builder Pattern)又称为创建者模式,建造者模式使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在该模式中存在一个Builder类,这个类是独立于其他对象的,在建造者模式中使用Builder类一步一步的构造最终的对象。

记得在刚开始接触web开发的时候,后端在接收到前端请求的参数时,后端需要不断的使用set函数来构造一个对象,比如现在需要构造一个Student类,但是我就写了这么一串又臭又长的代码。

Student student = new Student();
student.setId("id");
student.setName("1999single");
student.setGrade("17");
student.setMajor("软件工程");
... ...

是吧,看着头疼,写着也头疼。直到接触到了建造者模式,心中的太阳升起来了……

介绍

意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

何时使用:一些基本部件不会变,而其组合经常变化的时候。

如何解决:将变与不变分离开。

关键代码:产品:创建的实力,建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。

应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的
StringBuilder。

优点: 1、建造者独立,易扩展。 2、便于控制细节风险。

缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。

使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。

注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

结构

产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个滅部件。

抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法。

具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。

指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

实现

既然称作建造者模式,这里就建造一座房子好勒!假设一种房子有四种属性:几楼、什么样的门、什么样的窗户、外墙是什么颜色的。

/**
 * Product(产品角色)
 */
public class House {

    private String storey;

    private String door;

    private String window;

    private String color;

    public String getStorey() {
        return storey;
    }

    public void setStorey(String storey) {
        this.storey = storey;
    }

    public String getDoor() {
        return door;
    }

    public void setDoor(String door) {
        this.door = door;
    }

    public String getWindow() {
        return window;
    }

    public void setWindow(String window) {
        this.window = window;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "House{" +
                "storey='" + storey + '\'' +
                ", door='" + door + '\'' +
                ", window='" + window + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
}

还需要有一个建筑队,他们有着建造房子的技术。

/**
 * Builder(抽象建造者)
 */
public interface HouseBuilder {

    void buildStorey();

    void buildDoor();

    void buildWindow();

    void buildColor();

    House build();
}

public class ChinaHouseBuilder implements HouseBuilder {

    private House house;

    public ChinaHouseBuilder() {
        this.house = new House();
    }

    @Override
    public void buildStorey() {
        house.setStorey("5");
    }

    @Override
    public void buildDoor() {
        house.setDoor("steel door");
    }

    @Override
    public void buildWindow() {
        house.setWindow("steel window");
    }

    @Override
    public void buildColor() {
        house.setColor("white");
    }

    @Override
    public House build() {
        return house;
    }

}

此时还需要一个指挥的人,选取善于修建中式的房屋的建筑队还是善于修建复式房屋的建筑队,以及告诉建筑队该房屋需要建哪一些东西,比如我就觉得房子的外装没必要,不如把外装的钱省下来用在内装!!内在美才是真的美!!

public class Director {

    public House bulidHouse(HouseBuilder builder) {
        builder.buildStorey();
        builder.buildDoor();
        builder.buildWindow();
        builder.buildColor();
        return builder.build();
    }
}

最后当然就是老板出钱盖房子咯(调用)

public class Main {
    public static void main(String[] args) {
        Director director = new Director();
         House house = director.bulidHouse(new ChinaHouseBuilder());
         System.out.println(house);
    }
}

到这里一个简单的建造者模式就构建完成了,我们发现每一个Builder子类的都非常的独特(写得死),假如我要修建一个6楼的房子我又需要新建一个Builder子类,假如我不要窗户又得新建一个Builder子类,这也是建造者模式的缺点。

为了解决这个缺点,又产生了建造者模式的链式方法,同时也提高了代码编写的舒适度。

链式调用

有些朋友可能不熟悉什么是链式调用,咱们拿两个在实际项目中的例子来说明,一样就是看出所谓链式是什么意思。

// 安卓的一个加载图片的库,基于建造者模式和链式调用的思想诞生的。
Glide.with(context) // context为安卓开发中的上下文
    .load(internetUrl) // internetUrl网络图片的地址
    .into(targetImageView); // targetImageView为现实图片的一个组件/容器
    
// okhttp中创建request的方法
Request request = new Request.Builder()
                        .url(url) // 请求的url
                        .get() // 设置请求的方式为GET,相应的还有.post()方法
                        .build(); // 建造对象的最后一步

可以看出他们共同的特点就是不断的 .().().().().().() 写代码的舒适性立马提高,接下来我们来讨论下如何实现这样的链式结构

链式调用实现

我们还是使用上面的造房子的例子来说明

// product
public class House {

    private String storey;

    private String door;

    private String window;

    private String color;

    public House(String storey, String door, String window, String color) {
        this.storey = storey;
        this.door = door;
        this.window = window;
        this.color = color;
    }

    public House() {
    }

    public String getStorey() {
        return storey;
    }

    public void setStorey(String storey) {
        this.storey = storey;
    }

    public String getDoor() {
        return door;
    }

    public void setDoor(String door) {
        this.door = door;
    }

    public String getWindow() {
        return window;
    }

    public void setWindow(String window) {
        this.window = window;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "House{" +
                "storey='" + storey + '\'' +
                ", door='" + door + '\'' +
                ", window='" + window + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
    
    // product中静态的方法生成一个Builder
    public static HouseBuilder builder() {
        return new HouseBuilder();
    }

    // Builder
    public static class HouseBuilder {
        private String storey;

        private String door;

        private String window;

        private String color;

        public String getStorey() {
            return storey;
        }
        // 返回自身对象,用于下一步的调用
        public HouseBuilder storey(String storey) {
            this.storey = storey;
            return this;
        }

        public String getDoor() {
            return door;
        }

        public HouseBuilder door(String door) {
            this.door = door;
            return this;
        }

        public String getWindow() {
            return window;
        }

        public HouseBuilder window(String window) {
            this.window = window;
            return this;
        }

        public String getColor() {
            return color;
        }

        public HouseBuilder color(String color) {
            this.color = color;
            return this;
        }

        // 最后构建House
        public House bulid() {
            return new House(storey, door, window, color);
        }
    }
}

调用:

House house = House.builder().storey("2").window("not").door("kk").color("blue").build();

Lombok中的建造者模式(链式调用)

使用Lombok可以非常快速的实现类的建造者模式,我们还是对House进行操作

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@ToString
public class House {

    private String storey;

    private String door;

    private String window;

    private String color;
    
}

好的,写完了,晚安!使用Lombok就是这么方便,什么Builder什么Director都不要了。

再来看一下调用,也非常的简单。

House hh = House.builder()
        .storey("3")
        .color("black")
        .window("wooden")
        .door("wooden")
        .bulid();

Lombok实现建造者原理(简单说说)

首先我们反编译一下编译器生成的House.class得到

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package builderPattern;

public class House {
    private String storey;
    private String door;
    private String window;
    private String color;

    public static House.HouseBuilder builder() {
        return new House.HouseBuilder();
    }

    public House() {
    }

    public House(String storey, String door, String window, String color) {
        this.storey = storey;
        this.door = door;
        this.window = window;
        this.color = color;
    }

    public String getStorey() {
        return this.storey;
    }

    public String getDoor() {
        return this.door;
    }

    public String getWindow() {
        return this.window;
    }

    public String getColor() {
        return this.color;
    }

    public void setStorey(String storey) {
        this.storey = storey;
    }

    public void setDoor(String door) {
        this.door = door;
    }

    public void setWindow(String window) {
        this.window = window;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public static class HouseBuilder {
        private String storey;
        private String door;
        private String window;
        private String color;

        HouseBuilder() {
        }

        public House.HouseBuilder storey(String storey) {
            this.storey = storey;
            return this;
        }

        public House.HouseBuilder door(String door) {
            this.door = door;
            return this;
        }

        public House.HouseBuilder window(String window) {
            this.window = window;
            return this;
        }

        public House.HouseBuilder color(String color) {
            this.color = color;
            return this;
        }

        public House build() {
            return new House(this.storey, this.door, this.window, this.color);
        }

    }
}

为了便于阅读代码我删掉了一些于建造者模式无关的代码(使用注解后Lombok还会自动生成toString方法和hashCode方法),其实Lombok只是自动帮我们生成了代码而已,实际上还是这么一个思路:–>创建Bulider–>开始建造–>最后bulid。

至于Lombok是怎么实现动态生成代码,这就要追溯他的原理的,首先他的注解是用于source的,java编译成class后,注解就不存在于字节码文件中了,所以Lombok其实是在代码编译时期动态的生成代码插入到编译树中的。(关于编译原理不说太多了,这学期刚挂科。有时间再自己实现一下Lombok的@ToString注解,有兴趣的朋友可以关注一下我!挂科博主耻辱下线,晚安。)




❤❤❤ 只需要捐献一个赞就能实现孩子的成为架构师梦想👍 ❤❤❤
❤❤❤ 只需要捐献一个赞就能实现孩子的成为架构师梦想👍 ❤❤❤
❤❤❤ 只需要捐献一个赞就能实现孩子的成为架构师梦想👍 ❤❤❤

❤❤❤ 只需要捐献一个赞就能实现孩子的成为架构师梦想👍 ❤❤❤

❤❤❤ 只需要捐献一个赞就能实现孩子的成为架构师梦想👍 ❤❤❤

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值