设计模式——构建器(Builder)模式

来源:互联网 发布:linux deploy汉化版 编辑:程序博客网 时间:2024/04/28 13:43

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

【实际需求】

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

【问题分析】

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

方法一:重载构造器

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

public Exception()public Exception(String message)public Exception(String message, Throwable cause)public Exception(Throwable cause)protected Exception(String message, Throwable cause,     boolean enableSuppression,boolean writableStackTrace)

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

方法二:使用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);        }    }}

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

public static void main( String[] args ){    User.Builder builder = new User.Builder("lmy86263", "123");    User user = builder.userFullName("lmy86263").createDate(new Date()).builder();}

使用这种方式也可以灵活地调整类实例化的步骤,使类的实例化更有弹性,而且可以向用户隐藏内部的具体实现。这种方式在很多的设计中有很多的例子,比如Android中的AlertDialog类等。

0 0
原创粉丝点击