设计模式精讲之建造者模式

建造者模式是一种设计模式,用于将复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。通过建造者类和指挥类,客户端可以更简洁地创建对象,而无需关注具体的构建细节。简化版建造者模式进一步减少了代码复杂性,通过静态内部类和链式调用实现。此外,Lombok库提供的@Builder注解能快速实现建造者模式,使代码更为简洁。

建造者模式是什么?

建造者模式又叫做创建者模式,它将复杂的对象的创建过程与它的表示进行分离。建造者模式把复杂的创建过程进行了抽象,通过子类继承或者重载的方式,动态的创建对象,因此在使用建造者模式创建对象时,就不需要关心对象的创建过程了。

为什么要使用建造者模式?

  1. 建造者模式具有很好的封装性,它将构建和表示进行分离。
  2. 客户端不需要知道产品内部的组成细节,建造者之间相互独立,有利于系统的解耦。

什么时候使用建造者模式?

这一点非常重要,如果你学习一个知识都不了解它的应用场景,那么倒不如不学。

建造者模式一般用来创建比较复杂的对象,当对象中有较多并且复杂的属性时,就可以选择建造者模式来优化你的代码。

怎么使用建造者模式?

阅读完上面的内容,我们大概可以知道建造者模式

  1. 是为了创建一个对象;
  2. 对象一般是比较复杂的对象;
  3. 将创建对象的过程和对象的表示进行了分离

在不使用建造者模式之前,我们是如何创建对象的呢?没错,就是new,我们看一个例子,现在要创建一个房子对象,过程如下:

public class Main {
    public static void main(String[] args) {
        //直接new
        House house1 = new House();
        house1.setWall("白墙");
        house1.setDoor("木门");
        house1.setBed("木床");
        house1.setTable("木桌");   
    }
}

class House {
    private String wall; //墙
    private String door; //门
    private String table; //桌子
    private String bed; //床

    //省去setter和getter方法
    }
}

这种方式创建对象无疑是最简单的,但同样也存在非常多的问题,比如我们在创建House时必须要知道House中的所有属性,然后给每个属性一一赋值。讲的形象一点,当你想盖一座房子时,需要知道盖房子的具体细节,是不是很麻烦?

image-20210812221158358

其实在现实生活中,根本不需要这么麻烦,我们的目的很简单,就是想要一个房子,所以最简单的方式就是找到售楼处,把自己的喜欢的类型跟他们说一下,让售楼处给你一座合适的房子,如果房子还没有盖好,售楼处会联系开发商,让他们去盖房子,你这边不需要关心盖楼的细节,就等他们盖完交付到你手上即可。所以建造者模式也是从这个流程中抽象出来的一种设计模式。

传统的建造者模式

四个基本组成

我们看一下上面流程中必不可少的四个组成部分:

  1. 产品类:即我们最终要得到的对象,类比为房子。
  2. 建造者类:即直接参与构建产品的类,不需要跟客户端打交道,创建对象就是他们的任务,类比为开发商。
  3. 指挥类:直接跟客户端打交道,可以提供产品给客户端,但不参与创建对象,类比为售楼处。
  4. 客户端:使用设计模式前需要自己new产品,使用后直接找指挥类要产品。
产品类:房子
class House {
    private String wall; //墙
    private String door; //门
    private String table; //桌子
    private String bed; //床
    
    //省略setter和getter方法
}
建造者类:开发商

一般业务比较复杂的情况下,建造者会有很多种,会根据不同的业务定制自己的逻辑,但最终的目的还是建造产品,因此可以将建造者的核心功能抽象出来,个性化的定制可以通过子类实现。

//建造者的抽象类
abstract class Builder {
    //创建房子
    protected House house = new House();
    
    //核心功能,将创建好的房子提供给别人
    public House build(){
        return house;
    }
    
    //供子类实现个性化的需求,这里可以把方法名字起的通俗易懂些。
    abstract void buildWall(String wall);
    abstract void buildDoor(String door);
    abstract void buildTable(String table);
    abstract void buildBed(String bed);
}

//真正盖楼的阳光施工队
class SunBuilder extends Builder {

    @Override
    void buildWall(String wall) {
        house.setWall(wall);
    }

    @Override
    void buildDoor(String door) {
        house.setDoor(door);
    }

    @Override
    void buildTable(String table) {
        house.setTable(table);
    }

    @Override
    void buildBed(String bed) {
        house.setBed(bed);
    }
}

//还可以创建更多的施工队。。
指挥类:售楼处

连接开发商和客户的关键人物。

//售楼处
class SalesDirector {
    //一个private的开发商
    private Builder builder;

    public SalesDirector(Builder builder) {
        this.builder = builder;
    }
    
    //提供开发商盖好的房子
    public House providHouse() {
        return builder.build();
    }
}
客户端

客户只需要收房子即可,不需要创建

public class Main {
    public static void main(String[] args) {
        //阳光开发商盖楼
        SunBuilder sunBuilder = new SunBuilder();
        sunBuilder.buildBed("木床");
        sunBuilder.buildDoor("木门");
        sunBuilder.buildWall("白墙");
        sunBuilder.buildTable("木桌");

        //客户直接而从售楼处拿房子即可
        House house = new SalesDirector(sunBuilder).providHouse();
    }
}

到此一个标准的建造者模式就完成了,通过BuilderHouse进行创建,然后在由指挥类Director返回给客户端,好处呢很明显,将House的创建过程交给了Builder,进行了解耦。坏处呢也很明显,跟之前相比麻烦了不少,客户端的代码也变多了。

image-20210812223524145

其实这种方式我们并不常用,下面我们来让人神清气爽的的简化版建造者模式。

简化版的建造者模式

省去了指挥类,将建造者移动到产品类中。

  1. 在产品类中创建静态的建造者类,注意是静态内部类。
  2. 将产品类的属性复制到建造者类中。
  3. 在建造者类中为每一个属性创建赋值方法,并处理业务逻辑
  4. 在产品类中创建构造函数,参数为建造者类,并一一给属性赋值
  5. 在建造者类中创建产品对象的方法,一般是build方法

我们还是边看代码边理解:

class House {
    private String wall; //墙
    private String door; //门
    private String table; //桌子
    private String bed; //床

    //4.在产品类中创建构造函数,参数为建造者类,并一一给属性赋值
    private House (Builder builder) {
        this.wall = builder.wall;
        this.door = builder.door;
        this.table = builder.table;
        this.bed = builder.bed;
    }
    public static Builder newBuilder(){
        return new Builder();
    }

    //1.在产品类中创建静态的建造者类,注意是静态内部类。
    static class Builder {
        //2.将产品类的属性复制到建造者类中。
        private String wall; //墙
        private String door; //门
        private String table; //桌子
        private String bed; //床

        //3.在建造者类中为每一个属性创建赋值方法,并处理业务逻辑
        public Builder buildWall(String wall) {
            this.wall = wall;
            return this;
        }
        public Builder buildDoor(String door) {
            this.door = door;
            return this;
        }
        public Builder buildTable(String table) {
            this.table = table;
            return this;
        }
        public Builder buildBed(String bed){
            this.bed = bed;
            return this;
        }

        //5.在建造者类中创建产品对象的方法,一般是build方法
        public House build(){
            return new House(this);
        }
    }
}

客户端调用

public class Main {
    public static void main(String[] args) {
        House house = House.newBuilder()
                .buildBed("木床")
                .buildDoor("木门")
                .buildTable("木桌")
                .buildWall("白墙")
                .build();
    }
}

用这种方式即解决了客户端代码比较多的问题,也分离了对象的表示和创建过程。是不是感觉清爽了很多。不过你是否还有疑问,那我创建对象的时候不是依然要buildBed、buildTable、buildWall、buildDoor吗?由于这个案例只有简单的赋值业务,因此看不出特别大的明显,当House里面包含了非常多的类属性,有着很复杂的业务时,客户端这边看到的依然是这么点代码,更加复杂的创建过程可以写在buildXX中,而且这些方法的命名尽量设置为让调用方看一眼就知道什么意思,这样也会给复杂的业务降低不少的困难,代码的可读性也更加高。

最简单的建造者模式

如果感觉上面的建造者创建起来依然很麻烦,下面告诉大家一个非常简单的用法,不需要创建建造者,只需要使用一个注解即可。

image-20210812202743151

如果是maven项目首先要添加上一个依赖,如下:

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>

在产品类,也就是上面的House类上添加一个注解@Builder(toBuilder = true),既可实现上面的所有功能:

@Builder(toBuilder = true)
class House {
    private String wall; //墙
    private String door; //门
    private String table; //桌子
    private String bed; //床
}

客户端调用

public class Main {
    public static void main(String[] args) {
        House house = House.newBuilder()
                .buildBed("木床")
                .buildDoor("木门")
                .buildTable("木桌")
                .buildWall("白墙")
                .build();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值