解析《Effective Java》之多个构造器、Javabeans模式和Builder模式
最近看《Effective Java》这本被很多同行称为神作的书,但是这本书很多地方缺少了举例不好懂,下面是关于我对书上知识的理解。
一、《Effective Java》中文版2 ——第二章 第2条:遇到多个构造器参数时要考虑用构建器
原文语段:遗憾的是,JavaBeans模式自身有着很严重的缺点。因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于不一致状态的对象,将会导致失败,这种失败与包含错误的代码大相径庭,因此它调试起来十分困难。与此相关的另一点不足在于,JavaBeans模式阻止了把类做成不可变的可能(见第15条),这就需要程序员付出额外的努力来确保它的线程安全。
二、理解多个构造器、Javabeans模式
1、首先我们要知道构造器有个很大的局限性:它们都不能很好地扩展到大量的可选参数。举个简单的例子 人有:姓名、性别、年龄、国家、职业等属性。
public class Person {
private String name; //姓名
private String age; //年龄
private String sex; //性别
private String country; //国家
private String occupation; //职业
public Person(String name) {
this(name,null);
}
public Person(String name, String age) {
this(name,age,null);
}
public Person(String name, String age, String sex) {
this(name,age,sex,null);
}
public Person(String name, String age, String sex, String country) {
this(name,age,sex,country,null);
}
public Person(String name, String age, String sex, String country, String occupation) {
this.name = name;
this.age = age;
this.sex = sex;
this.country = country;
this.occupation = occupation;
}
}
类似是这样的构造方式,我们(程序员)一般习惯采用重叠构造器(telescoping constructor)模式。
当你想要创建实例的时候,就利用参数列表最短的构造器,但该列表中包含了要设置的所有参数:
Person person = new Person("xiaobai", "12", "男", "中国");
获取到的实例属性已经固定了不可变了。但这种方式有着明显的缺点就是:它们都不能很好的扩展到大量的可选参数。而且代码的可读性变差。
2、理解什么是Javabean
JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:(1)这个Java类必须具有一个无参的构造函数(2)属性必须私有化。(3)私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
public class Person {
private String name; //姓名
private String age; //年龄
private String sex; //性别
private String country; //国家
private String occupation; //职业
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getOccupation() {
return occupation;
}
public void setOccupation(String occupation) {
this.occupation = occupation;
}
}
JavaBeans模式阻止了把类不可做成可变,而且代码的可读性变的很强。但JavaBeans 模式自身有着严重的缺点: 那就是构造过程被分解到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。
举个例子来说,线程A创建Person实例并只给name用set赋值,线程B创建Person实例只给age用set赋值,这样两个线程创建出来的两个实例我们就无法用构造方法上的参数来判断属性状态是否相同(PS:person类用的无参方法。)。所以这样就能解释了《Effective Java》原文说:“因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。”
三、Builder模式
Builder模式:不直接生产想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端再builder对象上调用类似于setter方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生产不可变对象。
public class Person {
private String name; //姓名
private String age; //年龄
private String sex; //性别
private String country; //国家
private String occupation; //职业
public static class Builder {
private String name; //姓名
private String age; //年龄
private String sex; //性别
private String country; //国家
private String occupation; //职业
public Builder(String name, String age) {
this.name = name;
this.age = age;
}
public Builder sex(String val) {
sex = val;
return this;
}
public Builder country(String val) {
country = val;
return this;
}
public Builder occupation(String val) {
occupation = val;
return this;
}
public Person build() {
return new Person(this);
}
}
private Person(Builder builder) {
name = builder.name;
age = builder.age;
sex = builder.sex;
country = builder.country;
occupation = builder.occupation;
}
}
调用方式:
Person person = new Person.Builder("xiaobai","18").sex("男").country("中国").occupation("程序员").build();
这样编写的build编写的可读性与多个构造器相比更强。
builder像个构造器一样,可以对其参数强加约束条件。build方法可以检验这些约束条件。将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对它们进行检验,这一点很重要。
还有就是Java中传统的抽象工厂实现是Class对象,用newInstance方法充当build方法的一部分。这种用法隐含着许多问题。newInstance方法总是企图调用类的无参构造器,这个构造器甚至可能根本不存在。如果类没有可以访问的无参构造器,你也不会收到编译时错误。相反,客户端代码必须在运行时处理InstantiationException或者IllegalAccessException,这样既不雅观也不方便。newInstance方法还会传播由无参构造器抛出的任何异常,即使newInstance缺乏相应的throws子句。换句话说,Class.newInstance破坏了编译时的异常检查。上面讲过的Builder接口弥补了这些不足。
builder模式的不足在于:(1)为了创建西对象,必须先创建它的构建器。虽然创建构建器的开销在实践中可能不那么明显,但是在某些十分注重性能的情况下,可能就成问题了。
(2)Builder模式还比重叠构造器更加冗长,因此它只有在很多参数的时候才使用。
结语:
如果有看过此书的,或者有对文章有不同看法的欢迎在评论区评论。
解析《Effective Java》之多个构造器、Javabeans模式和Builder模式的更多相关文章
- Java设计模式(3)建造者模式(Builder模式)
Builder模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构 ...
- 《Effective Java》-——用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类.Singleton通常被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统.使类成为Singleton会使它的客户端测试变得十分困难,因为无法给Si ...
- Effective Java 之 --- 用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类,通常用来代表那些本质上唯一的系统组件,实现Singleton有三种方法: 1)公有静态成员是个final域,享有特权的用户可以调用AccessibleObje ...
- Java设计模式--------建造者模式(Builder模式)
Builder模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构 ...
- effective java 3th item2:考虑 builder 模式,当构造器参数过多的时候
yiaz 读书笔记,翻译于 effective java 3th 英文版,可能有些地方有错误.欢迎指正. 静态工厂方法和构造器都有一个限制:当有许多参数的时候,它们不能很好的扩展. 比如试想下如下场景 ...
- 《Effective Java第二版》总结
第1条:考虑用静态工厂方法代替构造器 通常我们会使用 构造方法 来实例化一个对象,例如: // 对象定义 public class Student{ // 姓名 private String name ...
- Builder模式在Java中的应用
在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成.那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Build ...
- Java设计模式之builder模式
Java设计模式之builder模式 今天学mybatis的时候,知道了SQLSessionFactory使用的是builder模式来生成的.再次整理一下什么是builder模式以及应用场景. 1. ...
- Builder模式在Java中的应用(转)
在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式,所构建的对象往往需要多步初始化或赋值才能完成.那么,在实际的开发过程中,我们哪些地方适合用到Builder模式呢?其中使用Build ...
随机推荐
- wpf mvvm模式下CommandParameter传递多参
原文:wpf mvvm模式下CommandParameter传递多参 CommandParameter一般只允许设置一次,所以如果要传递多参数,就要稍微处理一下.我暂时还没找到更好的方案,下面介绍的这 ...
- vue props 传输数值或boolean
字面量语法 vs 动态语法 初学者常犯的一个错误是使用字面量语法传递数值: <!-- 传递了一个字符串"1" --> <comp some-prop=" ...
- WPF MVVM系列文章
网上搜到了MSDN Magazine上Laurent Bugnion的系列文章. 以下为关于WPF的优秀实践,很有必要阅读. 很吸引人的标题有: IOC Containers and MVVM Mes ...
- 2 abp 领域层创建实体
领域层: LearningMpaAbp.Core项目 基础服务层:EntityFramework对应的项目 1 在领域层新建Tasks文件夹 在文件夹下新建Task类 但是注意 Task类必须要继 ...
- 使用xtrabackup实现MySQL主从复制
环境描写叙述 主从环境 项目 Master Slave OS版本号 SuSE 11sp1 x86_64 SuSE 11sp1 x86_64 MySQL版本号 官方版本号5.5.37 官方版本号5.5. ...
- 使用WPF实现3D场景[一]
原文:使用WPF实现3D场景[一] 在这篇文章里,将介绍如何实现一个简单的三维场景,一个三维的空间,包括空间内的三维物体的组合. 首先介绍一下一个三维场景里的基本元素: 先是定义一个简单的三维的场景环 ...
- easyui的datebox最简单的方法来格式化
看了网上有很多解决方案,我也写了一个比较简单的方法. 实现easyui的datebox格式化. 效果例如以下.用"++"隔开,看你喜欢用什么都能够. 1.html <span ...
- WPF: FishEyePanel/FanPanel - 自定义Panel
原文:WPF: FishEyePanel/FanPanel - 自定义Panel 原文来自CodeProject,主要介绍如何创建自定义的Panel,如同Grid和StackPanel. 1) Int ...
- WPF编游戏系列 之五 数据绑定
原文:WPF编游戏系列 之五 数据绑定 在上一篇通过用户控件将重复使用的控件封装为一个控件组,大大减少了C#代码数量,本篇继续对该控件组进行数据绑定,节省为每个控件赋值的工作.对于数据绑 ...
- JDK源码阅读——Vector实现
1 继承结构图 Vector同样继承自AbstractList,与ArrayList.LinedList一样,是List的一种实现 2 数据结构 // 与ArrayList一样,也是使用对象数组保存元 ...