通俗易懂的java设计模式之建造者模式

我是一个粉刷匠,粉刷本领强。我要把那新房子,刷得更漂亮。请问如何评价一个粉刷匠刷墙的质量,就是要求刷的地方不留白且均匀,也就是对细节的把控 上。今天介绍的建造者模式,它就是根据对象的每个细节来建造,落实到对象每一个属性上。

假如现在我们要构建一个会员对象,该会员对象有以下属性:

会员名称、会员等级、会员折扣(打几折)

那么我们该如何构建该对象?遇到这个问题其实我们最能想到的是什么方法,就是构造方法,最终就会实现一个如下效果。

sql 复制代码
Member member = new Member("普通会员", 5, 6);

那么这种传统的构造方法有什么问题呢。

1. 可读性差

上边的例子中,名称你或许很容易就能知道,但会员等级、折扣呢,只有到了对应的构造方法后才能确定张三的会员等级、折扣。

2. 不灵活

后期我想在加上一个会员权限,这个怎么加呢?有些属性是必要的,而有些属性不是必要的,我们又该如何构建。

聪明的你或许会想到用get/set,它确实能解决问题,但这样做就没有弊端了吗,它没有办法一次性构建完整对象,只能分步设置,这样很容易出错。

正因为传统的构造方法和get/set在构建复杂对象有很多问题,由此衍生出了建造者模式,它适用于当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数时的情况。


接下来就给大家介绍下建造者模式。

它首先有如下四个角色:

  • Product: 最终要生成的对象,例如 Member实例。
  • Builder : 构建者的抽象基类(有时会使用接口代替)。其定义了构建Product的抽象步骤,其实体类需要实现这些步骤。其会包含一个用来返回最终产品的方法Product getProduct()
  • ConcreteBuilder: Builder的实现类。
  • Director : 决定如何构建最终产品的算法. 其会包含一个负责组装的方法void Construct(Builder builder), 在这个方法中通过调用builder的方法,就可以设置builder,等设置完成后,就可以通过builder的 getProduct() 方法获得最终的产品。

废话少叙,直接上代码:

typescript 复制代码
public class BuilderTest {
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder();
        Director director = new Director(builder);
        Member normalMember = director.normalConstruct();
        System.out.println(normalMember.toString());
        Member advancedMember = director.advancedConstruct();
        System.out.println(advancedMember.toString());
        //剔除导演类
        Builder builder1 = new ConcreteBuilder();
        builder1.buildNormalMember();
        Member result = builder1.getProduct();
        System.out.println(result);
    }
}
// Builder
abstract class Builder{
    //创建产品对象
    protected Member member = new Member();

    public abstract void buildNormalMember();
    public abstract void buildAdvancedMember();

    public Member getProduct(){
        return member;
    }
}
// ConcreteBuilder
class ConcreteBuilder extends Builder{

    @Override
    public void buildNormalMember() {
        member.setName("普通会员");
        member.setLevel(1);
        member.setDiscount(9);
        member.setPermission(2);
    }

    @Override
    public void buildAdvancedMember() {
        member.setName("高级会员");
        member.setLevel(2);
        member.setDiscount(8);
        member.setPermission(1);
    }
}

class Director{
    private Builder builder;

    public Director(Builder builder){
        this.builder = builder;
    }
    public Member normalConstruct(){
        builder.buildNormalMember();
        return builder.getProduct();
    }
    public Member advancedConstruct(){
        builder.buildAdvancedMember();
        return builder.getProduct();
    }
}

// Product:最终要生成的对象
class Member{
    // 会员名称
    private String name;
    // 会员等级
    private Integer level;
    // 会员折扣
    private Integer discount;
    // 会员权限
    private Integer permission;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getLevel() {
        return level;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }

    public Integer getDiscount() {
        return discount;
    }

    public void setDiscount(Integer discount) {
        this.discount = discount;
    }

    public Integer getPermission() {
        return permission;
    }

    public void setPermission(Integer permission) {
        this.permission = permission;
    }

    @Override
    public String toString() {
        return "Member{" +
                "name='" + name + '\'' +
                ", level=" + level +
                ", discount=" + discount +
                ", permission='" + permission +
                '}';
    }
}

运行结果如下:

ini 复制代码
Member{name='普通会员', level=1, discount=9, permission=2}
Member{name='高级会员', level=2, discount=8, permission=1}
// 剔除导演类
Member{name='普通会员', level=1, discount=9, permission=2}

在传统的建造者模式中,它把构造的细节进行了封装(必要参数),这样我们只需要在使用的时候调用对应的方法进行对象构建即可,方便了后续的扩展。

可能你又有疑问了,我在很多源码上看到的建造者模式长这样呀,如下:

arduino 复制代码
Product product=new Product.Builder("XX")
                .setXXX("XXX")
                .setXXXX("XXXX")
                .build();

其实它算是传统建造者模式的变种,它省略了director 这个角色,将构建算法交给了client端,并将builder 写到了要构建的产品类里面,最后采用了链式调用。

代码如下:

kotlin 复制代码
public class Client{
    public static void main(String[] args) {
        Member member = new Member.Builder("普通会员", 1)
                .setDiscount(9)
                .setPermission(2)
                .build();
        System.out.println(member.toString());
    }
}

class Member {
    // 会员名称
    private String name; // 必须
    // 会员等级
    private Integer level; // 必须
    // 会员折扣
    private Integer discount; // 可选
    // 会员权限
    private Integer permission; // 可选

    public Member(Builder builder) {
        this.name = builder.name;
        this.level = builder.level;
        this.discount = builder.discount;
        this.permission = builder.permission;
    }

    @Override
    public String toString() {
        return "Member{" +
                "name='" + name + '\'' +
                ", level=" + level +
                ", discount=" + discount +
                ", permission=" + permission +
                '}';
    }

    public static class Builder{
        // 会员名称
        private String name; // 必须
        // 会员等级
        private Integer level; // 必须
        // 会员折扣
        private Integer discount; // 可选
        // 会员权限
        private Integer permission; // 可选

        public Builder(String name,Integer level){
            this.name = name;
            this.level = level;
        }

        public Member.Builder setDiscount(int discount) {
            this.discount = discount;
            return this;
        }
        public Member.Builder setPermission(Integer permission) {
            this.permission = permission;
            return this;
        }
        public Member build(){
            return new Member(this);
        }
    }
}

运行结果如下:

ini 复制代码
Member{name='普通会员', level=1, discount=9, permission=2}

我们要根据真实的业务场景灵活选用。

结束!

相关推荐
代码对我眨眼睛21 分钟前
springboot从分层到解耦
spring boot·后端
The Straggling Crow30 分钟前
go 战略
开发语言·后端·golang
ai安歌36 分钟前
【JavaWeb】利用IDEA2024+tomcat10配置web6.0版本搭建JavaWeb开发项目
java·开发语言·后端·tomcat·web·intellij idea
尘浮生1 小时前
Java项目实战II基于Java+Spring Boot+MySQL的作业管理系统设计与实现(源码+数据库+文档)
java·开发语言·数据库·spring boot·后端·mysql·spring
程序员阿鹏2 小时前
ArrayList 与 LinkedList 的区别?
java·开发语言·后端·eclipse·intellij-idea
java_heartLake3 小时前
微服务中间件之Nacos
后端·中间件·nacos·架构
LB_bei3 小时前
设计模式-行为型模式-命令模式
设计模式·命令模式
GoFly开发者4 小时前
GoFly快速开发框架/Go语言封装的图像相似性比较插件使用说明
开发语言·后端·golang
苹果酱05674 小时前
通过springcloud gateway优雅的进行springcloud oauth2认证和权限控制
java·开发语言·spring boot·后端·中间件
豌豆花下猫5 小时前
Python 潮流周刊#70:微软 Excel 中的 Python 正式发布!(摘要)
后端·python·ai