在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成。那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Builder模式来替代多参数构造函数是一个比较好的实践法则。

我们常常会面临编写一个这样的实现类(假设类名叫DoDoContact),这个类拥有多个构造函数,

DoDoContact(String name);

DoDoContact(String name, int age);

DoDoContact(String name, int age, String address);

DoDoContact(String name, int age, String address, int cardID);

这样一系列的构造函数主要目的就是为了提供更多的客户调用选择,以处理不同的构造请求。这种方法很常见,也很有效力,但是它的缺点也很多。类的作者不得不书写多种参数组合的构造函数,而且其中还需要设置默认参数值,这是一个需要细心而又枯燥的工作。其次,这样的构造函数灵活性也不高,而且在调用时你不得不提供一些没有意义的参数值,例如,DoDoContact("Ace", -1, "SH"),显然年龄为负数没有意义,但是你又不的不这样做,得以符合Java的规范。如果这样的代码发布后,后面的维护者就会很头痛,因为他根本不知道这个-1是什么含义。对于这样的情况,就非常适合使用Builder模式。Builder模式的要点就是通过一个代理来完成对象的构建过程。这个代理职责就是完成构建的各个步骤,同时它也是易扩展的。下面是改写自Effective Java里面的一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DoDoContact {
    private final int    age;
    private final int    safeID;
    private final String name;
    private final String address;
 
    public int getAge() {
        return age;
    }
 
    public int getSafeID() {
        return safeID;
    }
 
    public String getName() {
        return name;
    }
 
    public String getAddress() {
        return address;
    }
 
    public static class Builder {
        private int    age     = 0;
        private int    safeID  = 0;
        private String name    = null;
        private String address = null;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
   // 构建的步骤
        public Builder(String name) {
            this.name = name;
        }
 
        public Builder age(int val) {
            age = val;
            return this;
        }
 
        public Builder safeID(int val) {
            safeID = val;
            return this;
        }
 
        public Builder address(String val) {
            address = val;
            return this;
        }
 
        public DoDoContact build() { // 构建,返回一个新对象
            return new DoDoContact(this);
        }
    }
 
    private DoDoContact(Builder b) {
        age = b.age;
        safeID = b.safeID;
        name = b.name;
        address = b.address;
 
    }
}

最终,客户程序可以很灵活的去构建这个对象。

1
2
3
4
DoDoContact ddc = new DoDoContact.Builder("Ace").age(10)
                .address("beijing").build();
System.out.println("name=" + ddc.getName() + "age =" + ddc.getAge()
                + "address" + ddc.getAddress());
 

Builder模式好处和优点

使用Builder模式必然会导致写两遍相关属性的代码和SETTER方法,看起来有点吃力不讨好。然而需要看到的是,客户端代码的可用性和可读性得到了大大提高。与此同时,构造函数的参数数量明显减少调用起来非常直观。

Builder方法另外一个优势在于,单个builder构建多个对象时Builder参数可在创建期间进行调整,还可以根据对象不同而进行改变。这就像我越来越推崇的以“不变”应“万变”Builder模式特别适合那些属性个数很多的类,我认为没有必要给那些本不需要设置值传递参数(设置null)。

Builder模式在提高代码可读性的同时,使用IDE提供的代码补全功能也更加容易。Builder模式在与构造函数一起使用带来的更大优势在Josh Bloch的Effective Java第二版第2条中有详细阐述。

Builder模式的代价和缺点

使用Builder模式是肯定会增加代码量的。此外,尽管客户端的代码可读性明显改善,但随之而来的客户端代码变得更加冗长。我还是坚持这一观点:相比较参数数量的增加,相同类型的参数混在一起,可选参数的增加而言,改善代码可读性更有价值。

Builder会增加个类代码,这也意味着开发者在给类增加属性时有时会忘记给该属性添加支持的builder。为了克服这个问题,通常我会将builder嵌套到类中,这样可以很容易地发现哪个相关的builder需要更新。尽管忘记的风险依旧存在,但是这风险就像忘记给类的新属性增加toString()、 equals(Object)、 hashCode()或其他类基于是所有属性的方法一样。

在我的Builder实现中,我会用Builder的构造函数而不是set方法传递客户需要的属性。这样做的好处在于,对象总是能被一次完整的实例化,而不是靠开发人员调用时用set方法补充额外的属性完成实例化。这也体现了不可变性带来的好处。然而,相应地也会造成自己设定的属性方法可读性降低。

正如它名字表示的那样,Builder只是一个替代构造器的选择不能直接用于降低非构造函数方法的参数数量,但是结合参数对象的使用就能达到这一点。有关更多反对用Builder来进行对象构建的讨论可以参见 A dive into the Builder pattern上的相关评论

总结

构建对象时,如果碰到类有很多参数——其中很多参数类型相同而且很多参数可以为空时,我更喜欢Builder模式来完成。当参数数量不多、类型不同而且都是必须出现时,通过增加代码实现Builder往往无法体现它的优势。在这种情况下,理想的方法是调用传统的构造函数。再者,如果不需要保持不变,那么就使用无参构造函数调用相应的set方法吧。

Builder模式在Java中的应用的更多相关文章

  1. Builder模式在Java中的应用(转)

    在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成.那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Build ...

  2. Android Builder模式在开发中的应用

    最近在学习图片加载框架Glide的时候,被他精简的写法震惊了.一句话,就可以搞定. Glide.with(mContext) .load(url) .centerCrop() .placeholder ...

  3. Active Object 并发模式在 Java 中的应用--转载

    原文地址:http://www.ibm.com/developerworks/cn/java/j-lo-activeobject/ 本文主要从以下两个方面进行阐述: 使用 C++ 语言,来描述 Act ...

  4. 代理模式与java中的动态代理

    前言    代理模式又分为静态代理与动态代理,其中动态代理是Java各大框架中运用的最为广泛的一种模式之一,下面就用简单的例子来说明静态代理与动态代理. 场景    李雷是一个唱片公司的大老板,很忙, ...

  5. 10分钟了解 代理模式与java中的动态代理

    前言    代理模式又分为静态代理与动态代理,其中动态代理是Java各大框架中运用的最为广泛的一种模式之一,下面就用简单的例子来说明静态代理与动态代理. 场景    李雷是一个唱片公司的大老板,很忙, ...

  6. 浅谈代理模式与java中的动态代理

    代理模式的定义: 代理模式是一个使用律非常高的模式,定义如下: 为其他对象提供一种代理,以控制对这个对象的访问. 类图: 简单的静态代理: public interface IRunner{ //这是 ...

  7. Java设计模式(3)建造者模式(Builder模式)

    Builder模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构 ...

  8. Java Builder 模式,你搞懂了么?

    加油.png 前言:最近闲来无事的时候想着看看一些平常用的三方库源码,没想到看了之后才知道直接撸源码好伤身体,一般设计优秀的开源库都会涉及很多的设计模式,就比如 android 开发使用频繁的 okH ...

  9. 同事写了一个疯狂的类构造器,我要疯了,Builder 模式都不会么?!

    疯狂的类构造器 最近栈长在做 Code Review 时,发现一段创建对象的方法: Task task = new Task(112, "紧急任务", "处理一下这个任务 ...

随机推荐

  1. linux的常用文件系统格式

    文件系统指文件存在的物理空间.在Linux系统中,每个分区都是一个文件系统,都有自己的目录层次结构.Linux的最重要特征之一就是支持多种文件系统,这样它更加灵活,并可以和许多其它种操作系统共存.Vi ...

  2. python学习之day6,常用标准模块

    1.时间模块 time import time #时间戳转字符串格式 a = time.time() print(a) #打印时间戳 b = time.localtime(a) #把时间戳转换成时间对 ...

  3. bzoj4196

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1376  Solved: 785[Submit][Stat ...

  4. C#-WebForm-点击网页中的按钮后跳转到其他页面是怎么实现的?

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. VS2015中SharedProject与可移植类库(PCL)项目

    转自:http://www.tuicool.com/articles/beaMZv3 今天闲里偷空看了点Connect大会的视频,C# 6.0的新语法.EF7的支持非关系型数据库.Windows商店应 ...

  6. Android源码——Activity组件的启动过程

    根Activity启动过程 Launcher启动MainActivity的过程主要分为6个步骤: 一.Launcher向ActivityManagerService发送一个启动MainActivity ...

  7. JavaScript系列文章:变量提升和函数提升

    第一篇文章中提到了变量的提升,所以今天就来介绍一下变量提升和函数提升.这个知识点可谓是老生常谈了,不过其中有些细节方面博主很想借此机会,好好总结一下. 今天主要介绍以下几点: 1. 变量提升 2. 函 ...

  8. JS 中如何将<br/> 替换成 /n

    JS 中如何将<br/> 替换成 /n function a() { var data = "aaaa<br/>bbbb<br/>cccc"; ...

  9. iOS - drawRect致内存增加

    GPU VS CPU iOS - 软件绘图 自定义"斑马线背景"View,重写drawRect绘制斑马线: ⚠️ 仅仅添加这一个View,内存就比正常增加了3-5M之间. 测试源代 ...

  10. 移动端阻止body滚动

    一些移动设备有缺省的touchmove行为,比如说经典的iOS overscroll效果,当滚动超出了内容的界限时就引发视图反弹 阻止滚动: css: body{ height:100%; overf ...