变种 Builder 模式:优雅的对象构建方式

读完本文你将了解到:

    • 帅气的 Builder 链式调用
    • 常见的两种构建方式
      • 常见的构建方式之一定义多个重载的构造函数
      • 常见的构建方式之二使用 setter 方法挨个构造
    • 优雅的构建方式变种 Builder 模式
    • Android Studio 中使用插件自动生成 变种 Builder 模式代码
    • 总结
    • Thanks

帅气的 Builder 链式调用

在日常开发中,经常可以看到这样的代码:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

或者

new AlertDialog.Builder(this)
            .setTitle("hello")
            .setMessage("I‘m shixinzhang")
            .setIcon(R.drawable.bg_search_corner)
            .setCancelable(true)
            .setOnCancelListener(new DialogInterface.OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialog) {
                    //...
                }
            })
            .show();

可以看到这样链式调用看起来好整齐啊,Builder 模式早有耳闻,今天就来详细了解一下。

常见的两种构建方式

在日常开发中,我们经常需要给某个对象的变量赋值,这个赋值的过程称为 对象的构建。

比如现在有个 Person 类,它有几个成员变量:

//固定不变的对象,一般变量需要声明为 final
private final String mName;     //必选,final 类型需要在 构造器中初始化,不允许不初始化它的构造器存在
private String mLocation;       //可选
private String mJob;            //可选
private String mHabit;          //可选

常见的构建方式之一:定义多个重载的构造函数

public class PersonOne {
    //固定不变的对象,一般变量需要声明为 final
    private final String mName;     //必选,final 类型需要在 构造器中初始化,不允许不初始化它的构造器存在
    private String mLocation;       //可选
    private String mJob;            //可选
    private String mHabit;          //可选

    public PersonOne(String name) {
        mName = name;
    }

    public PersonOne(String location, String name) {
        mLocation = location;
        mName = name;
    }

    public PersonOne(String name, String location, String job) {
        mName = name;
        mLocation = location;
        mJob = job;
    }

    public PersonOne(String name, String location, String job, String habit) {
        mName = name;
        mLocation = location;
        mJob = job;
        mHabit = habit;
    }
}

这种方式的优点:简单。

看起来很简单嘛,每个构造函数都需要啥参数,第几个参数是干什么的,看一下就知道了,嗯,很直观!

等等!这是你站在开发者的角度的结果!

使用者使用时可得仔细了解你每个构造函数,否则一不小心填错顺序也不知道。

而且如果有十几个属性,我靠,你见过有十几个参数的构造函数吗?

所以缺点是

只适用于成员变量少的情况,太多了不容易理解、维护。

常见的构建方式之二:使用 setter 方法挨个构造

吸取上面的教训,我不在构造方法里穿参数了,改成用 set 方法挨个构造,可以了吧。

public class PersonTwo {
    //固定不变的对象,一般变量需要声明为 final
    private final String mName;     //必选,final 类型需要在 构造器中初始化,不允许不初始化它的构造器存在
    private String mLocation;       //可选
    private String mJob;            //可选
    private String mHabit;          //可选

    public PersonTwo(String s) {
         this.mName = s;
    }

    public String getName() {
        return mName;
    }

    public String getLocation() {
        return mLocation;
    }

    public void setLocation(String location) {
        mLocation = location;
    }

    public String getJob() {
        return mJob;
    }

    public void setJob(String job) {
        mJob = job;
    }

    public String getHabit() {
        return mHabit;
    }

    public void setHabit(String habit) {
        mHabit = habit;
    }
}

这种方式也是常见的构造方式,它的好处是:易于阅读,并且可以只对有用的成员变量赋值;

缺点是:

  • 成员变量不可以是 final 类型,失去了不可变对象的很多好处;
  • 对象状态不连续,你必须调用 4 次 setter 方法才能得到一个具备 4 个属性值得变量,在这期间用户可能拿到不完整状态的对象。

而且使用起来也不好看:

    PersonTwo personTwo = new PersonTwo("shixin");
    personTwo.setJob("洗剪吹");
    personTwo.setLocation("温州");
    personTwo.setHabit("嘿嘿嘿");

如果有 N 个属性岂不是要 personTwo.setXXX N 回?不优雅!

即使把 setXXX 方法返回值改成当前构造类,但还是不满足最重要的缺点的第二点:

用户可能拿到不完整状态的对象。

什么意思呢?

这种方式是 先创建对象、后赋值,用户不知道什么时候拿到的对象是完整的,构建完成的。很有可能你只 set 了一两个属性就返回了,一些必要的属性没有被赋值。

优雅的构建方式:变种 Builder 模式

为了解决上述两种构建方式,伟大的程序员们创造出了 变种 Builder 模式

先来看看用 变种 Builder 模式怎么实现上述 Person 对象的构建吧:

public class PersonThree {
    //固定不变的对象,一般变量需要声明为 final
    private final String mName;     //必选,final 类型需要在 构造器中初始化,不允许不初始化它的构造器存在
    private String mLocation;       //可选
    private String mJob;            //可选
    private String mHabit;          //可选

    /**
     * 构造方法的参数是它的 静态内部类,使用静态内部类的变量一一赋值
     * @param builder
     */
    public PersonThree(Builder builder) {
        this.mName = builder.mName;
        this.mLocation = builder.mLocation;
        this.mJob = builder.mJob;
        this.mHabit = builder.mHabit;
    }

    /**
     * PersonTree 的静态内部类,成员变量和 PersonTree 的一致
     */
    public static class Builder{
        private final String mName;     //必选,final 类型需要在 构造器中初始化,不允许不初始化它的构造器存在
        private String mLocation;       //可选
        private String mJob;            //可选
        private String mHabit;          //可选

        /**
         * 含必选参数的构造方法
         * @param name
         */
        public Builder(String name) {
            mName = name;
        }

        public Builder setLocation(String location) {
            mLocation = location;
            return this;
        }

        public Builder setJob(String job) {
            mJob = job;
            return this;
        }

        public Builder setHabit(String habit) {
            mHabit = habit;
            return this;
        }

        /**
         * 最终构建方法,返回一个 PersonTree 对象,参数是当前 Builder 对象
         * @return
         */
        public PersonThree build(){
            return new PersonThree(this);
        }
    }
}

可以看到,变种 Builder 模式包括以下内容:

  • 在要构建的类内部创建一个静态内部类 Builder
  • 静态内部类的参数与构建类一致
  • 构建类的构造参数是 静态内部类,使用静态内部类的变量一一赋值给构建类
  • 静态内部类提供参数的 setter 方法,并且返回值是当前 Builder 对象
  • 最终提供一个 build 方法构建一个构建类的对象,参数是当前 Builder 对象

调用代码:

    new PersonThree.Builder("shixinzhang")
            .setLocation("Shanghai")
            .setJob("Android Develop")
            .setHabit("LOL")
            .build();

变种 Builder 模式目的在于减少对象创建过程中引入的多个构造函数、可选参数以及多个 setter 过度使用导致的不必要的复杂性。

好处就是文章开头所说的:

  • 看起来很整齐;
  • 先赋值,后创建对象。

最终调用 build() 方法才创建了构建类的对象,保证了状态的完整性。

缺点嘛,就是需要额外写的代码多了点。

不过还好,有前辈开发出了 Android Studio 插件来拯救我们。

Android Studio 中使用插件自动生成 变种 Builder 模式代码

第一步:下载插件 Inner Builder:

第二步:重启 Andriod Studio;

第三步:写好要构建的类的变量:

比如:

public class PersonTest {
    private final String mName;
    private int mAge;
    private String mLocation;

}

第四步:按 Control + Insert (Mac :command + N):

第五步:在弹出的 Generate 对话框中选择 Builder:

第六步:选中要使用 Builder 构建的对象,然后勾选使用的配置,点击 OK :

public class PersonTest {
    private final String mName;
    private int mAge;
    private String mLocation;

    private PersonTest(Builder builder) {
        mName = builder.mName;
        mAge = builder.mAge;
        mLocation = builder.mLocation;
    }

    public static final class Builder {
        private String mName;
        private int mAge;
        private String mLocation;

        public Builder() {
        }

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

        public Builder mAge(int mAge) {
            this.mAge = mAge;
            return this;
        }

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

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

基本上和我们手写的一致,是不是方便很多呢?

总结

经典的 Builder 模式定义为:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

它的重点在于:抽象出对象创建的具体步骤到一个接口,通过调用不同的接口实现,从而得到不同的结果。

Builder 模式在 Android 开发中演变出了 变种 Builder 模式,它除了具备经典构建者模式的功能,还简化了构建的过程,使得创建过程更加简单、直观。

Thanks

《Android 高级进阶》这本书的目录值得一看

时间: 2024-11-03 03:44:53

变种 Builder 模式:优雅的对象构建方式的相关文章

Builder模式的实现

Builder模式适用于构造对象时,需要多个参数的情况.对于多个参数,用方法名也能很好的说明用途. 但为了构造对象,就必须先创建它的Builder构建器,在某些十分注重性能的情况下,就可能是一个性能问题. 实现方式1: public class Book { private final String author;     //必须参数 private final String name; //必须参数 private final String ISBN; //必须参数 private fina

builder模式-积木系列

代码高效的表达意图,是优秀代码的基本标准,所以在刚刚写代码的时候我们总是被教育方法的名字,变量字段的名字要尽可能表达出在程序中的含义. 在<重构和模式>中提到的creation,就是讲构造实例的方式封装变为可以表达含义的方法提供给客户端使用,从而保证了在阅读客户端代码时,可以清洗的明白代码在做什么,而不需要进入构造方法中看创建的逻辑. 创建的模式中有工厂方法 和 builder模式.无论用什么方式,我们想达到的目的就是讲构造过程和使用过程分离. builder类图:

Item 2---遇到构造器具有多个参数时,要考虑用构建器;Builder模式

问题,面对这种一个构造器具备多个参数的问题,现有的做法是使用重叠构造器的方式,该方式存在的问题: public class NutritionFacts { private final int servingSize; // (mL) required private final int servings; // (per container) required private final int calories; // optional private final int fat; // (

Builder模式详解及其在Android开发中的应用

一.引言 在Android开发中,采用Builder模式的代码随处可见,比如说Android系统对话框AlertDialog的使用或者是Android中的通知栏(Notification)的使用,又比如说在一些常用的第三方库中也随处可见其踪迹,比如说一些常用的网络请求库如OkHttp或者是retrofit,又或者是图片加载库Glide中也不缺乏它的应用. 为什么Builder模式在Android或是Java开发中这么火呢?因为它相较于构造函数或者是Get/Set方法,它的灵活性和封装性上都比较有

关于Builder模式

builder模式是什么? builder模式是设计模式的一种,从名词方面来解析就是构建. builder模式优势在哪里?它比javaBean模式具有更强的灵活性. 实例分析javaBean与builder模式构建实例比较 package system.info;/* * 以业务类型类作为样板,包含javaBean和Builder两种构建方式 */public class TBusinessType {      //javaBean 模式 start    private Long id;  

Java建造者模式(Builder模式)

Builder模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到. 为何使用建造者模式 是为了将构建复杂对象的过程和它的部件解耦.注意:是解耦过程和部件. 因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮.方向盘.发动机,还

Java Builder模式 体验(二)

在上篇文章中,对Java Builder模式的使用体验主要是从Builder对构造器改造方面的优秀特性来说的,感觉并没有从Java Builder模式本身的功能和作用去写,因此决定再从Builder模式的作用以及在项目开发中的使用来体验下.     Builder 模式,即建造者模式,顾名思义,这个模式可能更多的使用在产品的组装中使用,具体说就是在软件产品的组件或模块组装的时候使用.     感觉网络上比较好的解释有:     建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使

设计模式之Builder模式

一.感性认识 二.Builder模式 1.定义 一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示.即构建过程相同,但是子部件却不相同. 2.结构说明 Builder: 创建者接口(也可定义为抽象类),定义创建一个Product对象的所需各个部件的操作 ConcreteBuilder: 具体的创建者实现,实现各个部件的创建并负责组装Product对象的各个部件:同时提供一个让用户访问的组装完成的Product对象的方法 Director: 导向者,主要用来使用Builder接

Java 实现建造者(Builder)模式

建造者(Builder)模式是创建型模式,创建了一个对外隐藏创建过程的产品,使用组合的方式,由指挥者(Director)来决定建造的流程 public interface Builder { void buildPartA(); void buildPartB(); void buildPartC(); } public class BuilderImpl implements Builder { @Override public void buildPartA() { System.out.p