Builder设计模式

Builder设计模式

Buider设计模式作为23种设计模式之一,其定义为:讲一个复杂对象的构建与它的表示相分离,使得同样的构建过程可以创建不同的表示。

通常情况下,Builder模式中有以下角色:

  • Builder(建造者)
    Builder角色负责定义用于生成实例的接口,因此Builder类一般为抽象类,其中的方法为抽象方法
  • ConcreteBuilder(具体的建造者)
    ConcreteBuilder角色是负责实现Buider角色所定义的抽象方法的类,通常定义了生成实例被调用时所执行的具体操作。此外,在该角色中通常还定义了获取最终生成结果的方法。
  • Director(监工)
    Director角色负责使用Builder角色的接口来生成实例,也就是说它调用的Builder角色中的抽象方法,因此它并不依赖于ConcreteBuilder角色,这样也有利于降低耦合性和增加复用性。

下面用代码具体演示一下。

  1. 首先创建Builder角色。
public abstract class Builder {
    abstract void buildPart1();
    abstract void buildPart2();
    abstract void buildPart3();
    abstract void buildPart4();
}
  1. 创建ConcreteBuilder角色
public class ConcreteBuilder1 extends Builder {
    private ProductBean bean = new ProductBean();
    
    @Override
    void buildPart1() {
        bean.setPart1("石头");
    }
    
    @Override
    void buildPart2() {
        bean.setPart2("水泥");
    }
    
    @Override
    void buildPart3() {
        bean.setPart3("塑料");
    }
    
    @Override
    void buildPart4() {
        bean.setPart4("铁");
    }
    
    public ProductBean getResult() {
        return bean;
    }
}
public class ConcreteBuilder2 extends Builder {
    private ProductBean bean = new ProductBean();

    @Override
    void buildPart1() {
        bean.setPart1("水晶");
    }

    @Override
    void buildPart2() {
        bean.setPart2("活性炭");
    }

    @Override
    void buildPart3() {
        bean.setPart3("木头");
    }

    @Override
    void buildPart4() {
        bean.setPart4("铜");
    }

    public ProductBean getResult() {

        return bean;
    }
}

其中ProductBean为具体需要构建的对象

public class ProductBean {
    private String part1;
    private String part2;
    private String part3;
    private String part4;
    
    public String getPart1() {
        return part1;
    }

    public void setPart1(String part1) {
        this.part1 = part1;
    }

    public String getPart2() {
        return part2;
    }

    public void setPart2(String part2) {
        this.part2 = part2;
    }

    public String getPart3() {
        return part3;
    }

    public void setPart3(String part3) {
        this.part3 = part3;
    }

    public String getPart4() {
        return part4;
    }

    public void setPart4(String part4) {
        this.part4 = part4;
    }

    @Override
    public String toString() {
        return "ProductBean{" +
                "part1='" + part1 + '\'' +
                ", part2='" + part2 + '\'' +
                ", part3='" + part3 + '\'' +
                ", part4='" + part4 + '\'' +
                '}';
    }
}
  1. 创建Director角色
public class Director {
    private Builder builder;

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

    public void construct(){
        builder.buildPart1();
        builder.buildPart2();
        builder.buildPart3();
        builder.buildPart4();
    }
}

截止到现在,Builder设计模式中所需要的角色都已经创建完成,接下来我们在测试类中进行一下测试:

public class Test {

    public static void main(String args[]){
        ConcreteBuilder1 concreteBuilder1 = new ConcreteBuilder1();
        Director director = new Director(concreteBuilder1);
        director.construct();
        System.out.println("ConcreteBuilder1: "+ concreteBuilder1.getResult().toString() );

        ConcreteBuilder2 concreteBuilder2 = new ConcreteBuilder2();
        Director director1 = new Director(concreteBuilder2);
        director1.construct();
        System.out.println("ConcreteBuilder2: " + concreteBuilder2.getResult().toString() );
    }

}

执行上面的测试代码,可以看到控制台输出的结果为:
ConcreteBuilder1: ProductBean{part1=‘石头’, part2=‘水泥’, part3=‘塑料’, part4=‘铁’}
ConcreteBuilder2: ProductBean{part1=‘水晶’, part2=‘活性炭’, part3=‘木头’, part4=‘铜’}

由此我们可以看到,由于具体的ConcreteBuilder的实现方式不同,同样的构建过程创建了两种不同的表示,调用者在构建具体的对象时只需要创建对应的ConcreteBuilder角色并作为参数传递给Director角色,然后调用Director角色的construct()方法即可,并不需要关注具体的构建过程。并且由于Director角色并不依赖于ConcreteBuilder角色,即使具体的构建过程改变也只需要增加新的ConcreteBuilder角色,并不会影响Director角色的复用性。但是经过多年的实践演化,在Java&Android开发中广泛使用的是它的一个变种。作为一个Android开发人员,最熟悉的莫过于AlertDialog的使用,语句如下:

AlertDialog dialog = new AlertDialog.Builder(this)
                .setTitle("标题")
                .setMessage("对话框内容")
                .setIcon(R.drawable.ic_launcher)
                .create();
        dialog.show();

再比如网络请求框架OkHttp的请求封装:

private Request(Builder builder){
	this.url = builder.url;
	this.method = builder.method;
	this.head = builder.head;
	this.body = builder.body;
	this.tag = builder.tag != null ? builder.tag : this ;
}

经典的Buider模式重点在于抽象出对象的创建步骤,并通过调用不同的具体实现从而得到不同的结果,而变种的Buider模式的目的在于减少对象创建过程中韵如的多个重载构造函数、可选参数以及setters过度使用导致的不必要的复杂性。下面我们通过一个简单的User对象的创建过程为例子进行说明:

public class User {

   private final String firstName;       //必选

    private final String lastName;        //必选

    private final String gender;          //可选

    private final int age;                //可选

    private final String phone;           //可选

}

从代码中我们可以看出User类有五个属性,其中firstName和lastName为必选的也就是必须要初始化的,而其他三个属性为可选属性,由于都是不可变的(final),所以必须要在构造函数中对这些属性进行初始化,那么我们为了满足所有情况就必须创建多个构造函数:

public class User {

    private final String firstName;       //必选

    private final String lastName;        //必选

    private final String gender;          //可选

    private final int age;                //可选

    private final String phone;           //可选

    public User(String firstName, String lastName) {
        this(firstName,lastName,"");
    }

    public User(String firstName, String lastName, String gender) {
        this(firstName,lastName,gender,0);
    }

    public User(String firstName, String lastName, String gender, int age) {
       this(firstName,lastName,gender,age,"");
    }

    public User(String firstName, String lastName, String gender, int age, String phone) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.gender = gender;
        this.age = age;
        this.phone = phone;
    }
}

这种构造函数的方式虽然简单,但只适用于只有少量几个属性的情况,随着User类属性的增加,代码将变得越来越难以阅读和维护。更严重的是对于类的使用者而言,代码将变得更加难以使用。
另外一种可先方案是遵循JavaBean的规范,定义一个默认的无参构造函数,并为类的每个属性定义getter和setter方法:

public class User {

    private String firstName;       //必选

    private String lastName;        //必选

    private String gender;          //可选

    private int age;                //可选

    private String phone;           //可选

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

这种方案的好处是易于阅读和维护,使用者可以创建一个空实例,并只设置感兴趣的属性值,但是这个方案存在两个缺点:

  • User类是可变的,从而失去了不可变对象的很多好处(编程常识之一:我们应该尽量将属性值定义为不可变的)
  • User类的实例状态不连续,如果你想创建一个同时具有五个属性值的类实例,那么直到第五个属性值的set函数被调用时,该实例才具有完整连续的状态,这就意味着实例的调用者可能会看到该实例的不连续状态。为了同时兼顾前两种方案的优势,变种的Builder模式应运而生:
public class User {

    private final String firstName;       //必选

    private final String lastName;        //必选

    private final String gender;          //可选

    private final int age;                //可选

    private final String phone;           //可选

    private User(Builder builder) {
        firstName = builder.firstName;
        lastName = builder.lastName;
        gender = builder.gender;
        age = builder.age;
        phone = builder.phone;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String getGender() {
        return gender;
    }

    public int getAge() {
        return age;
    }

    public String getPhone() {
        return phone;
    }

    public static final class Builder {
        private final String firstName;
        private final String lastName;
        private String gender;
        private int age;
        private String phone;

        public Builder(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
           
        }

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

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

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

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

从上面的代码可以看出以下几点:

  • User类的构造函数是私有的,这就意味着调用者不能直接实例化这个类
  • User类是不可变的
  • UserBuilder的构造函数只接收必选的属性值作为参数,并且只有必选的属性值设置为final,以此保证它们在构造函数中初始化

下面我们在测试类中进行测试:

public class Test {

    public static void main(String args[]){
        User user = new User.Builder("san","zhang")
                .gender("man")
                .age(23)
                .phone("18800000000")
                .build();
        
        System.out.print(user.toString());
    }

}

控制台输出 User{firsaName=‘san’, lastName=‘zhang’, gender=‘man’, age=23, phone=‘18800000000’}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值