设计模式—–Builder模式

Java基础

浏览数:165

2019-5-9

AD:资源代下载服务

前言

近日,看到Myabtis中组件中SqlSessionFactory由SqlSessionFactoryBuilder().build()生成时,且采用Builder模式,遂记录学习之。

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

1、什么是Builder模式?

  (1)对于复杂的对象,如果只是用构造方法创建的话,构造方法中会存在很多的逻辑,那么我们可以一步步有秩序构建它,从而降低复杂度;

  (2)对于复杂的对象,使用者不必知道其内部是如何实现的清下,逐步构造需要的实例对象;

2、什么情况下使用Builder模式?

  (1)将一个复杂对象的构建与它的表示分离,即相同的构造过程可以有不同表示;

  (2)当有多个构造器且需要传入不同的参数表示不同的产品时(即可以弥补工厂模式等无法选择多参数的缺点)

  (3)传入参数情况比较灵活且复杂的情况,或者说一开始不需要明确参数的情况。

  (4)框架中很多Configuration配置都会用到Builder模式。

3、具体使用Builder例子

(1)以前经常通过不同构造器传入不同的参数构造不同复杂的对象,比如我们现在需要一个User的不同情况对象

  • 只有id和name
  • 有id、name、age
  • 有id、name、age、address
public class User {
    private int id;
    private String name;
    private int age;
    private String address;
    
    //不同的构造器传入不同的参数,创造不同的复杂的产品
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public User(int id, String name, int age, String address) {
        this.id = id;
        this.name = name;
        this.age= age;
        this.address = address;
    }
    
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

测试类这样编写:

public class Main {
    public static void main(String[] args) {
        //情况1:id、姓名
        User user = new User(1, "Zhangsan");
        //情况2:id、姓名、年龄
        User user2 = new User(2,"Lisi",22);
        //情况3:id、姓名、年龄、地址
        User user3 = new User(3,"Wangwu",23,"Beijing");
        //情况4:id与年龄不清楚,很容易混淆,必须得查看源码或者文档才能知道哪个参数位置id/年龄
        User user4 = new User(24,"Wangwu",24,"Beijing");
    }
}

OK,这样是没有问题的,但是会有以下弊端:

  • 就好比情况4。当传入的参数很多并且没有说明文档的情况下,之后阅读完源码后才能更好地使用。那么就有可能对源码进行修改,这样就违背了在应用中一个好的设计模块的原则,即设计模式中的开闭原则(Open-Closed Principle, OCP,对修改封闭)
  • 每种情况都得编写一个的构造器,没有一点的灵活度。再比如这里address属性是可选的,可以不传入,那么灵活度同样很低!

(2)接下来就使用Builder模式创建,注意:Builder主要采用Java静态内部类

/**
 * 利用Builer模式灵活面对复杂对象的创建
 * @author Lijian
 *
 */
public class User2 {
    private int id;
    private String name;
    private int age;
    private String address;
    
    private User2(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.age = builder.age;
        this.address = builder.address;
    }
    static class Builder{
        private int id;
        private String name;
        private int age;
        private String address;
        //灵活选择参数
        public Builder setId(int id) {
            this.id = id;
            return this;
        }
        public Builder setName(String name) {
            this.name = name;
            return this;
        }
        public Builder setAddress(String address) {
            this.address = address;
            return this;
        }
        public Builder setAge(int age) {
            this.age = age;
            return this;
        }
        //最后build返回User2对象
        public User2 build() {
            return new User2(this);
        }
    }
}

测试类:

public class Main {
    public static void main(String[] args) {
        //通过build创建了User2对象,之后通过setXXX方法可灵活初始化属性,最后build返回对象
        User2 user = new User2.Builder().setId(1).setName("Lijian").setAge(22).build();
        //情况1:id、姓名
        User2 user2 = new User2.Builder().setId(2).setName("Zhangsan").build();
        //情况2:id、姓名、年轻、地址
        User2 user3 = new User2.Builder().setId(2).setName("Lisi").build();
        //情况3:id与age很明显能区分
        User2 user4 = new User2.Builder().setId(23).setAge(23).build();
    }
}

通过setXXX()方法灵活选择参数,最后build()方法“闭合”返回对象。很适用于复杂对象的创建,此处让我想起Java8新特性中的Stream API(https://blog.csdn.net/mynewclass/article/details/80393308)的一个特点:懒加载。是的,有点“懒加载”的味道,不需要立马指定属性,也不会立马生效,之后最后的操作build()才会生效!

 

作者:JJian