网上99%的建造模式都说错了,只看这一篇就够

简介: 网上99%的建造模式都说错了,只看这一篇就够

1. 创建对象的几种方法

上一讲我们介绍了工厂模式

可以用工厂类来构造一个对象

原来这么多年都用错了工厂模式

其实,归根结底

创建一个对象,都需要调用构造方法

Java编码推荐:每一个类都需要一个无参数的构造方法

而且默认无需定义

例如:

Class Car{
    String model; // 型号
    Float price; // 零售价
}

那么它就一定可以使用 new Car() 来创建

在 Factory 里也是如此,只是把 new 的过程交给了工厂方法

2. 单例用法的实现

单例是最简单的设计模式

其实在Java体系中

只要一个对象是 static 那它仅会有一个

所以 单例模式基本上只有一种写法

public class MessageService{
    private static MessageService service; // 唯一存在的对象
    private MessageService(){
        // TODO
    }
    public static MessageService getInstance(){
        if(service==null){ // 首次调用时会创建
            service=new MessageService();
        }
        return service;
    }
}

先用 private 限定构造方法

因此,这类无法被其他类 new 创建

然后通过一个 public static 方法,用于对象创建调用

这样此对象有且只有一个


小知识:static 是静态的变量

很多同学可能不明白

其实可以换一种说法

叫他全局变量

它的生命周期是无限的

从程序启动到结束

跨越时间和对象、方法的界限

3. 如果构造方法有参数?

前面的案例都有一个大前提

构造方法必须是无参数的

不然呢,在 new 的时候还有参数

那该传几个参数?顺序呢?怎么传入呢?

都会是问题

因此,前面都是开胃菜

今天着重介绍的是 builder 建造者 模式

4. builder 的一般用法

可能有些同学之前学过一些

在古老教程中

builder 模式的构成

包括 Builder、ConcreteBuilder、Director、Product 等部分

还给他们取了不同的名字

比如 产品、建产品的工人、监工等等


我们今天简化一下

就强调两个点:Builder 和 Product

Product 代表的是建的对象,决定建什么东西

Builder 代表建东西的类,决定怎么建

与工厂做类,可以认为 Builder 就是 建 Product 的工厂

但做法大相径庭


4.1 多参数构造的困境

Builder 的应用场景

是为了解决有参数构造的问题

并且参数往往很复杂

举例:

Class Car{
    String model; // 型号
    Float price; // 零售价
    public Car(String model, Float price){
        this.model=model;
        this.price=price;
    }
}

以上这个例子,就是构造对象必须初始化值的一种做法

如果我们要new一个对象

往往这样写:

Car wl_mianbao=new Car("五菱EV50", 98800F);
    Car df_mianbao=new Car("东风小康", 79999F);

但程序员都知道

需求总是改

忽然有一天

需要增加属性

Class Car{
    String model; // 型号
    Float price; // 零售价
    String color; // 颜色
    public Car(String model, Float price){
        this.model=model;
        this.price=price;
    }
    public Car(String model, Float price, String color){
        this.model=model;
        this.price=price;
        this.color=color;
    }
}

为了增加一个属性color

我们得重新写一个构造器,支持3个参数

并且为了兼容老版本的代码

2个参数的构造器还得留着


这种写法是不健康的

试想现在是3个参数,万一又加呢?

我是不是还得写4参数、5参数的构造器

而且参数之间还可能有排列组合、顺序的问题

代码越写就越混乱

4.2 通过setter解决

以上问题可以转换一个思路

用setter方法解决

如下:

Class Car{
    String model; // 型号
    Float price; // 零售价
    String color; // 颜色
    public void setModel(String model){
        this.model=model;
    }
    public void setPrice(Float price){
        this.price=price;
    }
    public void setColor(String color){
        this.color=color;
    }
}

回归了无参数构造

但是需要依次调用setter

才能完整的创建一个对象

Car xiaomian=new Car();
xiaomian.setModel("五菱");
xiaomian.setPrice(98800F);
xiaomian.setColor("星光金");

这种方法就规避了参数多样化的问题

但setter主要的作用是字段赋值

在这里借用了取巧的方法

虽然能解决大部分问题

但仍然有一些不完美的地方

例如:

哪些参数是可有可无的?

会不会漏传参数?

等等

4.3 builder模式

这个时候,就需要引入builder类

通常是建一个内部类来解决

public class Car {
    String model; // 型号
    Float price; // 零售价
    String color; // 颜色
    private Car(Builder carbuilder) {
     this.model=carbuilder.model;
     this.price=carbuilder.price;
     this.color=carbuilder.color;
    }
    public static class Builder{
        private String model;
        private Float price;
        private String color;
        public Builder(String model) { // 必要参数构造
         this.model=model;
        }
        public Builder() { // 可选参数构造
        }
        public Builder model(String model) {
         this.model=model;
         return this;
        }
        public Builder price(Float price) {
         this.price=price;
         return this;
        }
        public Builder color(String color) {
         this.color=color;
         return this;
        }
        public Car build() {
         return new Car(this);
        }
    } 
}

调用时,变成了这样:

Car xiaocar=new Car.Builder()
            .model("五菱")
            .price(98800F)
            .color("星光金")
            .build();

对比一下,setter和builder的区别

主要体现在构造调用的方法

setter的前后文是无关的

而builder是一种链式调用的形式

直到最后一句 build() 走完

才真正创建了目标对象


看一下大家常用的类,举两个例子

例子一:JWT的令牌构造

JwtBuilder builder = Jwts.builder()
  .setId(id)
  .setSubject(subject) / 主题
  .setIssuer(userid) // 签发者
  .setIssuedAt(now) // 签发时间
  .signWith(key); // 签名算法以及密匙
  .setExpiration(expDate) // 过期时间
  .compact();

例子二:工作流的实例启动

ProcessInstance instance = runtimeService.createProcessInstanceBuilder();
 .processDefinitionId(processdefinitionid);
 .name(processname);
 .businessKey(key);
 .tenantId(tenantid);
 .start();

这种分步骤创建的方式

从阅读上很清晰

可以将一个复杂的步骤化简为零

所以对程序员来说

可以降低调用的复杂度

5. builder代码太多了,怎么办

刚才我们经历了一个简单类型

逐步复杂的过程

其实建一个普通类型还没什么

但builder类的代码有点太多了

还要写内部类,很多人不习惯

其实只要使用 lombok 就可以了

首先maven引用

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

然后在类上只要增加一个注解即可

@Builder
public class Car {
    String model; // 型号
    Float price; // 零售价
    String color; // 颜色
}

调用方法:

Car xiaocar=Car.builder()
            .model("五菱")
            .price(98800F)
            .color("星光金")
            .build();

是不是很简单了呢?

总结

很多设计模式都是来自平时遇到的问题

通过一些编程小技巧来解决问题

建造者模式应对的是构造参数较多、参数可变的情形

后续的系列中会介绍更加复杂的结构性模式

敬请期待吧

相关文章
|
存储 前端开发 JavaScript
潮玩宇宙大逃杀无聊猿卷轴模式系统开发详细规则丨步骤需求丨方案项目丨技术架构丨源码功能
确定游戏类型和规则:明确无聊猿卷轴模式游戏类型和游戏规则,包括敌人类型、地图设计、任务类型、战斗机制等。
|
5月前
|
存储 Web App开发 运维
发布、部署,傻傻分不清楚?从概念到实际场景,再到工具应用,一篇文章让你彻底搞清楚
部署和发布是软件工程中经常互换使用的两个术语,甚至感觉是等价的。然而,它们是不同的! • 部署是将软件从一个受控环境转移到另一个受控环境,它的目的是将软件从开发状态转化为生产状态,使得软件可以为用户提供服务。 • 发布是将软件推向用户的过程,应用程序需要多次更新、安全补丁和代码更改,跨平台和环境部署需要对版本进行适当的管理,有一定的计划性和管控因素。
1218 1
|
存储 编解码 数据处理
还在为搞不懂笔记本电脑参数而苦恼么?一篇文章就够啦
还在为搞不懂笔记本电脑参数而苦恼么?一篇文章就够啦
280 4
|
设计模式 SQL Java
有点狠有点猛,我用责任链模式重构了业务代码
文章开篇,抛出一个老生常谈的问题,学习设计模式有什么作用? 设计模式主要是为了应对代码的复杂性,让其满足开闭原则,提高代码的扩展性 另外,学习的设计模式 一定要在业务代码中落实,只有理论没有真正实施,是无法真正掌握并且灵活运用设计模式的 这篇文章主要说 责任链设计模式,认识此模式是在读 Mybatis 源码时, Interceptor 拦截器主要使用的就是责任链,当时读过后就留下了很深的印象(内心 OS:还能这样玩)
|
Java 程序员 开发者
只用一行代码,你能玩出什么花样?
只用一行代码,你能玩出什么花样?
91 1
|
uml 开发者 Windows
推荐5款冷门小工具,看一看有没有你喜欢的?
每个人的电脑中都会安装很多软件,可能还保留着很多不为人知的冷门软件。不过虽然冷门,但绝不意味着低能,相反很多冷门软件的功能十分出色。闲话少说,接下来我就给大家推荐5款冷门小工具,看一看有没有你喜欢的。
186 0
推荐5款冷门小工具,看一看有没有你喜欢的?
|
算法 NoSQL API
到底该不该看源码(懂这三点儿就够了)
1、不要为了看源码而看源码 2、代码积累到一定程度,遇到问题自然就去查源码了,然后你就看懂了 3、两年内不要刻意去看源码,可以点开简单了解一下就行,前两年疯狂做项目就行了,后期项目做的多了,你自己就会有疑问,每次写代码就会问自己为什么要这样写?底层的原理是什么?很自觉的带着问题就去看源码了,如果你没有这样的疑问,那说明你也不适合去看源码了,写写业务代码,了了一生
183 0
|
域名解析 网络协议 网络架构
一篇文章,只用看三遍,终生不忘网络分层!
一篇文章,只用看三遍,终生不忘网络分层!
159 0
一篇文章,只用看三遍,终生不忘网络分层!
|
域名解析 网络协议 网络架构
一篇文章,只用看三遍,终生不忘网络分层
一篇文章,只用看三遍,终生不忘网络分层
140 0
一篇文章,只用看三遍,终生不忘网络分层
|
搜索推荐
搭建相亲源码,小功能有大作用之关注功能
搭建相亲源码,小功能有大作用之关注功能