设计模式之构建器设计模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mn_kw/article/details/82379278

在面向对象的编程中对象的创建是最基本的动作,但是创建对象的方法有很多种,但是归根结底都是直接或者间接使用类的构造器完成实例的创建,包括静态工厂方法、JavaBean方式或者下面的要说的构建器模式,但是对于不同的情况,使用这几种方法各有利弊,这里使用一个实际的问题来引出这种对比。

【实际需求】

对一个包含10几个参数的类进行实例化,其中有些参数不是必须的,但是有些参数必须存在。

【问题分析】

首先对于实例化的这个动作,对于有编程经验的人再简单不过,第一个直接的想法就是写一个包含所有参数的构造器,但是回想一下这样真的好吗?每次初始化类的时候都会把自己弄晕,这个参数究竟是什么意思?

方法一:重载构造器

接触过Exception类构造器写法的人可能选择重载多个构造器的方法,如下

  •  

然后通过构造器之间的调用来完成初始化,但是当面对大量的实例参数时,需要扩展很多的构造器,显然这种方式的扩展性很差。

方法二:使用JavaBean方式

这种方式其实是不通过构造器传递参数,而是通过setter方法,如下:

public class User {
    private Integer userId;
    private String userName;
    private String userPwd;
    private String userFullName;
    private Date createDate;

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setUserPwd(String userPwd) {
        this.userPwd = userPwd;
    }

    public void setUserFullName(String userFullName) {
        this.userFullName = userFullName;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }
}
  •  

这种方式是我们最熟悉的,在我了解构建器模式之前一直使用的就是这种方式,因为这种方式对于编程者是友好的, 但是在使用的过程中,我就有过一些怀疑,如果调用10个参数的setter方法来实例化该类,但是出于一些原因,这10个调用中只执行了5个,那么这个类怎么办?这不是就出问题了吗?尤其是在多线程的环境下,任何对setter方法的调用都可能出错。

这也是《Effective Java》中所说的会使JavaBean出于不一致的状态的原因,而且还需要使用额外的手段来保证线程安全,因为JavaBean实例是可变的类。

方法三:使用构建器(Builder) 模式

这种方法对编程者非常友好,但是同时也能解决JavaBean对象面向多线程环境的固有缺陷,提高安全性。它是通过以下几种方式完成的:

  • 为了解决安全性问题,使用final限制属性不可变并移除setter方法等;
  • 为了解决可读性和扩展性问题,通过使用静态嵌套类,在其中设置可变参数方法等;

具体的实现看如下实例。

【具体实现】

public class User {
    // 所有属性为final, 保证该类为不可变类
    // 在构造器实例化的时候完成,不能在方法中完成
    private final String userName;
    private final String userPwd;
    private final String userFullName;
    private final Date createDate;

    // 构造器是私有的,确保类实例化只能通过builder模式
    // 并且不提供setter方法
    private User(Builder builder){
        this.userName = builder.userName;
        this.userPwd =builder.userPwd;
        this.userFullName =builder.userFullName;
        this.createDate =builder.createDate;
    }

    // 静态嵌套类,不能直接访问User的非静态变量,保证安全
    public static class Builder{
        // 必选参数为final, 在实例化Builder类的时候必须提供
        // 而不能由方法来完成
        private final String userName;
        private final String userPwd;

        // 可选参数可以提供默认参数
        private String userFullName = "default";
        private Date createDate = new Date();

        // 包括全部必选参数,它们初始化必须提供参数
        public Builder(String userName, String userPwd){
            this.userName = userName;
            this.userPwd = userPwd;
        }

        // 可选参数方法, 提高扩展性
        public Builder userFullName(String userFullName){
            this.userFullName = userFullName;
            return this;
        }

        public Builder createDate(Date createDate){
            this.createDate = createDate;
            return this;
        }

        // 实例化外部参数
        public User builder(){
            return new User(this);
        }
    }
}
  •  

在外部使用的时候,实例化如下:

扫描二维码关注公众号,回复: 3246420 查看本文章
public static void main( String[] args ){
    User.Builder builder = new User.Builder("lmy86263", "123");
    User user = builder.userFullName("lmy86263").createDate(new Date()).builder();
}
  •  

使用这种方式也可以灵活地调整类实例化的步骤,使类的实例化更有弹性,而且可以向用户隐藏内部的具体实现。

猜你喜欢

转载自blog.csdn.net/mn_kw/article/details/82379278