经典 Builder / 变种 Builder 模式及自动化生成代码插件

3,640 阅读5分钟

Builder模式是一种广泛使用的设计模式。

将一个复杂对象的构建与它的表示分立,这样在调用相同构建的过程中可以创建不同的表示

Builder模式分二种,一种是经典的Builder模式,第二种是变种Builder模式,而现在Android开发普遍使用的是第二种的变种Builder模式,下面我们一一来介绍。

---------------------------------我是分割线,不分不舒服------------------------

经典的Builder模式

经典Buider模式分为四块:

  • Product:被构造的复杂对象。
  • Builder:抽象接口。
  • BuilderImpl:抽象接口的具体实现。
  • Director:接口的构造者和使用者。

这张图是我其它地方看到偷的。嘎嘎

举个最简单的例子。
现在有个厂要生产不同的饼干,有方形的,圆形的等。

我们先建立饼干的类

public class Cookies {
    private String shape;

    public String getShape(){
        return shape;
    }

    public void setShape(String shape){
        this.shape = shape;
    }
}

然后我们创建Builder接口

public interface Builder{
    public void setShape();
    public Cookies getCookies();
}

然后实现Builder接口,比如创建一个会建立方形饼干的SquareCookiesBuilder和一个会建立圆形饼干的RoundCookiesBuilder

public class SquareCookiesBuilder implements Builder{
    private Cookies cookies;
    @Override
    public SquareCookiesBuilder(){
        this.cookies = new Cookies();
    }

    @Override
    public void setShape(){
        this.cookies.setShape("方形");
    }

    @Override
    public Cookies getCookies(){
        return this.cookies;
    }
}



public class RoundCookiesBuilder implements Builder{
    private Cookies cookies;
    @Override
    public RoundCookiesBuilder(){
        this.cookies = new Cookies();
    }

    @Override
    public void setShape(){
        this.cookies.setShape("圆形");
    }

    @Override
    public Cookies getCookies(){
        return this.cookies;
    }
}

最后创建Director类

public class Director {

    private Builder builder;

    public Director(Builder builder){
            this.builder = builder;
    }

    public void createCookies(){
        this.builder.setShape()
    }

    public Cookies getCookies(){
        return this.builder.getCookies();
    }
}

这样就是
比如获取方形的饼干

new Director(new SquareCookiesBuilder()).createCookies().getCookies()

比如获取圆形的饼干。

new Director(new RoundCookiesBuilder()).createCookies().getCookies()

二者的区别就是对Director传入不同形状饼干的Builder的实现类。
而Director的对象调用的方法都是createCookies()和getCookies()

所以经典的Builder模式重点在于抽象出对象创建的步骤,并通过调用不同的具体实现类从而得到不同的结果,而变种的Builder模式的目的在于减少对象创建过程中引入的多个重载构造函数,可选参数以及setters过度使用导致的不必要的复杂性

--------------------------我是变种分割线O(∩_∩)O~----------------------------

变种Builder模式

我们一步步来。比如还是Cookies举例。就单纯的还是形状。

public class Cookies {
    private final String shape;
}

(一般来说,我们尽量将属性值定义为不可变的。总不能饼干都已经做成方的了。再把它改成圆形吧)

那这时候怎么对这个shape赋值呢。你可能会想到

  • 构造函数

因为参数是final类型了。所以必须在构造函数中进行初始化,否则不能编译通过

public class Cookies {
    private final String shape;

    pubic class Cookies(String shape){
        this.shape = shape;
    }
}

这样看是没问题。但是如果我们不是饼干,是一个人:Person类。它有name,gender,age三个属性。但是用户并不是要每个属性都要输入的。这时候就要建立多个构造函数。

public class Person {
    private final String name;
    private final String gender;
    private final String age;

    pubic class Person(String name){
        this(name,"男","20");
    }

    pubic class Person(String name,String gender){
        this(name,gender,"20")
    }

    pubic class Person(String name,String gender,String age){
        this.name = name;
        this.gender = gender;
        this.age = age;
    }


}

这种构造函数虽然简单,但是当属性多了的时候。代码就会变得不易维护,而且构造函数里面的参数的顺序也很容易弄错。当参数有五个。你还记得第几个参数要填年龄?记得第几个参数要填姓名?

  • getters 和 setters 函数
public class Cookies {
    private String shape;

    public void setShape(String shape){
        this.shape = shape;
    }

    public String getShape(){
        return this.shape;
    }
}

优点:你可以建立对象,然后对你想要修改的属性进行修改,比如有二个属性,你可以只要调用你想修改的属性的set方法就可以进行修改。
缺点:
1.因为是set方法,所以shape的参数不在是final修饰了,因为你本身可以多次调用set方法。这样Cookies类就变成可变类了。失去了不可变类的好处了。
2.比如刚那个Person类,有三个属性。当你要给它的对象赋值这三个属性的时候,就要

Person person = new Person();
person.setName("青蛙ing");
person.setAge("20");
person.setGender("男");

失去一种连贯的感觉,而且这还只有三个属性。要是10个。你这样一行行的set方法写十遍??

----------------------我是主角分割线(我是主角!!)--------------------
还是以上面的Person类为例子。

public class Person {

    private final String name;
    private final String gender;
    private final String age;

    private Person(Builder builder) {
        name = builder.name;
        gender = builder.gender;
        age = builder.age;
    }

    public String getName() {
        return name;
    }

    public String getGender() {
        return gender;
    }

    public String getAge() {
        return age;
    }

    public static final class Builder {
        private String name;
        private String gender;
        private String age;

        public Builder() {
        }

        public Builder name(String val) {
            name = val;
            return this;
        }

        public Builder gender(String val) {
            gender = val;
            return this;
        }

        public Builder age(String val) {
            age = val;
            return this;
        }

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

从上述代码可以看出:
1.Person类的构造函数是私有的。这样就不能直接实例化这个类
2.Person类是不可变的。里面的属性都是final的。只能在构造函数中初始化。然后提供了属性的get函数,可以去获取值。
3.连贯性,这个Person的创建是:
new Person.Builder().name("青蛙ing").gender("男").age("20");
就问你这么写爽不爽。!!


------------------------------我是插件介绍线------------------------------------
这一段我就不自己截图了。
就引用别人的插件介绍了。
感谢CSDN的拭心
从他的文章里面拿了插件介绍的图片和内容
blog.csdn.net/u011240877/…

变种Builder模式自动化生成

1.下载插件 InnerBuilder:


2.重启 Andriod Studio;

3.写好要构建的类的变量:

比如:

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

}

4.按 Control + Insert (Mac :command + N):


5.在弹出的 Generate 对话框中选择 Builder:


6.选中要使用 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);
        }
    }
}