平常开发中需要写很多domain类,bean等java类,每个类中可能需要写一些构造方法,但每次开发时时用到什么构造可能写下,偶尔会产生这样的想法,这些构造器的写法,能不能像工厂模式呀,或者其它设计模式一样,更通用一些,代码写的更优雅一些呢!巧的是,正好最近在看《Effective java》这边书,有个优化项正好时这个,所以这里参考下《Effective java》,总结下书中写,小记一下。
多重叠构造器模式
在我们平常的开发中,静态工厂模式,构造器等方法是我们常用的一种是自己代码“更优美”的写法。但写多后,不难发现它们都有一个共同的局限性--不能很好的扩展到大量的可选参数。举一个通俗一点的例子:我们平常炒一道菜,基本都会包含”油“,”盐“,”味精“常用的调料,有时依照不同的口味或者地区,我们可以再向菜中添加辣椒(喜欢吃辣的),糖(江浙沪这一带)。这时我们统计这些调味品时,油盐味精是必然的选项,都是有值的(每道菜含多少克),而辣椒,糖等调料就是可能值为0的。
针对上面的调味品含量,写一个类,我可能就直接通过不同的构造器来写了,也就是多重叠构造器模式。如第一个构造器写一个全量参数,第二个构造器有一个可选项,第三个构造器有两个可选项,以此类推。。。
public class Foot {
private final int oil;
private final int salt;
private final int aginomoto;
private final int chili;
private final int sugar;
public Foot(int oil, int salt, int aginomoto) {
this(oil, salt, aginomoto, 0);
}
public Foot(int oil, int salt, int aginomoto, int chili) {
this(oil, salt, aginomoto, chili, 0);
}
public Foot(int oil, int salt, int aginomoto, int chili, int sugar) {
this.oil = oil;
this.salt = salt;
this.aginomoto = aginomoto;
this.chili = chili;
this.sugar = sugar;
}
}
但要创建Foot的对象实例时,就利用参数列表中最短的构造器,但该构造器包含了要设置的所有参数。我们的本意是传我们所需要的参数,不需要的参数就不用传值了。我们Foot类有五个属性,所以使用起来并不是那么繁琐,但如果我们遇到很多属性时,根本无法用此方法写。《Effective java》书中说:“重叠构造器模式可行,但是当有许多参数时,客户端代码很难编写,并且仍然很难阅读“。当有很多参数的类型一样时,我们把参数值写乱,编译器并不会报错,程序运行时就会出现错误的行为。
javaBeans模式
这样我们就产生出第二种替代方法,javaBeans模式。我们通过一个无参构造来创建一个实例,然后通过set方法来给类中的属性赋值。
public class Foot {
private int oil;
private int salt;
private int aginomoto;
private int chili;
private int sugar;
public void setOil(int oil) {
this.oil = oil;
}
public void setSalt(int salt) {
this.salt = salt;
}
public void setAginomoto(int aginomoto) {
this.aginomoto = aginomoto;
}
public void setChili(int chili) {
this.chili = chili;
}
public void setSugar(int sugar) {
this.sugar = sugar;
}
}
这种set方法弥补了重叠构造模式的不足。但缺点是:1构造过程被分到几个调用中,构造过程中的JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于不一致的对象,将会导致失败,调试起来十分困难;2JavaBean模式阻止类把类做成不可变的困可能,需要程序员付出额外的努力来确保线程安全。
Builder模式
Builder模式可以保证安全性和可读性,拥有重叠模式和JavaBean模式的优点。Builder模式不直接生成想要的对象,而是让客户端利用必要的参数调用构造,得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数,最后通过build方法生成不可变的对象。
public class Foot {
private final int oil;
private final int salt;
private final int aginomoto;
private final int chili;
private final int sugar;
private static class Builder {
private int oil;
private int salt;
private int aginomoto;
private int chili;
private int sugar;
public Builder(int oil, int salt, int aginomoto) {
this.oil = oil;
this.salt = salt;
this.aginomoto = aginomoto;
}
public Builder chili(int param) {
chili = param;
return this;
}
public Builder sugar(int param) {
sugar = param;
return this;
}
public Foot build() {
return new Foot(this);
}
}
public Foot(Builder builder) {
this.oil = builder.oil;
this.salt = builder.salt;
this.aginomoto = builder.aginomoto;
this.chili = builder.chili;
this.sugar = builder.sugar;
}
}
创建对象:Foot foot = new Foot.Builder(1, 1, 1).chili(1).sugar(0).build();
其中builder像个构造器一样,可以对其参数强加约束条件。与构造器相比,builder可以有多个可变的参数,它可以单独的设置每一个参数的值,可变参数有几个,它就可以设置几个。设置类参数的builder生成了一个很好的抽象工厂。
Builder模式的不足,为了创建对象就必须创建它的构造器,也会有性能问题,它比重叠构造更加的冗长。所以它适合在更多的参数时使用。
使用Builder模式的客户端代码会更容易阅读和维护编写,构建器也比JavaBeans更加安全。