使用Builder模式创建复杂可选参数对象
在新建对象时,若需要对大量可选参数进行赋值,最常见的做法是JavaBeans模式,即调用一个无参构造方法创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。代码示例如下:
public class Complex {
private int size;
private int color;
private int range = 0;
private int num = 0;
public Complex(int size, int color) {
this.size = size;
this.color = color;
}
public void setRange(int range) {
this.range = range;
}
public void setNum(int num) {
this.num = num;
}
}
这种模式需要的代码语句繁琐,而且这种做法阻止了把类做成不可变的可能。更加简洁的一种方式是通过多个构造方法去新建对象。比如下面的代码:
public class Complex {
private int size;
private int color;
private int range = 0;
private int num = 0;
public Complex(int size, int color) {
this.size = size;
this.color = color;
}
public Complex(int size, int color, int range) {
this.size = size;
this.color = color;
this.range = range;
}
// public Complex(int size, int color, int num) {
// this.size = size;
// this.color = color;
// this.num = num;
// }
public Complex(int size, int color, int range, int num) {
this.size = size;
this.color = color;
this.range = range;
this.num = num;
}
}
当参数越来越多时,这种方式就会使类的结构变得臃肿,难以维护,在使用时也需要去逐个理解每种构造方法的参数意义。并且在上述示例中,也是无法打开被注释掉的构造方法。
有没有一种更好的创建大量可选参数对象的方式?答案是Builder模式。先来看下面的示例代码:
public class NutritionFacts {
private final int size;
private final int color;
private final int num;
private final int range;
public static class Builder {
private int size;
private int color;
private int range = 0;
private int num = 0;
public Builder(int size, int color) {
this.size = size;
this.color = color;
}
public Builder num(int num) {
this.num = num;
return this;
}
public Builder range(int range) {
this.range = range;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
this.color = builder.color;
this.num = builder.num;
this.size = builder.size;
this.range = builder.range;
}
public static void main(String[] args) {
NutritionFacts build = new NutritionFacts.Builder(2, 1)
.num(3).range(4).build();
}
}
通过观察示例代码,不难发现。这种模式的本质是不直接生成想要的对象,而是通过链式编程构建一个参数完备的构造器对象,最终通过调用构造器对象的build()方法来生成不可变的目标对象。
显而易见,链式编程的秘诀在于从实例化对象开始,每次调用成员方法都会返回自身对象。
上述示例略微繁琐,若是追求简便,似乎有更好的另一种写法,示例代码如下:
public class NutritionFacts {
private int size;
private int color;
private int num;
private int range;
public static class Builder {
private NutritionFacts nutritionFacts = new NutritionFacts();
public Builder(int size, int color) {
this.nutritionFacts.size = size;
this.nutritionFacts.color = color;
}
public Builder num(int num) {
this.nutritionFacts.num = num;
return this;
}
public Builder range(int range) {
this.nutritionFacts.range = range;
return this;
}
public NutritionFacts build() {
return nutritionFacts;
}
}
public static void main(String[] args) {
NutritionFacts facts = new NutritionFacts.Builder(2, 1)
.num(3).range(4).build();
}
}
写到这里,笔者打算扩展下内部类与静态内部类的相关知识。
内部类与静态内部类
观察下面的静态内部类的声明,static关键字位于class关键字之前(顺序颠倒会报错),说明static的规则优先于class的规则。理解了这一点,就不难理解如何使用静态内部类。
public class InnerClass {
public class Inner{
public Inner(){
//..
}
}
public static class StaticInner{
static int i = 10;
public StaticInner(){
//..
}
}
}
对于内部类的实例化:
因为内部类属于外部类的非静态成员,所以首先需要实例化外部类,其次因为内部类也属于类,使用之前故也需要实例化。
public static void main(String[] args) {
new InnerClass().new Inner();
}
对于静态内部类的实例化:
因为static关键字必须声明在class之前,所以可知static的规则优先于class的规则。在处理类的静态成员时,优先使用“类名.静态成员”的形式,其次,再考虑实例化或其它变量引用。
public static void main(String[] args) {
new InnerClass.StaticInner();
int i = InnerClass.StaticInner.i;
}
本文参考资料:《Effective Java》
版权声明:凡未经本网站书面授权,任何媒体、网站及个人不得转载、复制、重制、改动、展示或使用本网站的局部或全部的内容或服务,或在非本网站所属服务器上建立镜像。如果已转载,请自行删除。同时,我们保留进一步追究相关行为主体的法律责任的权利。我们希望与各媒体合作,签订著作权有偿使用许可合同,故转载方须书面/邮件申请,以待商榷。