设计模式之构造器和模板方法
摘要
本文是设计模式学习的系列文章之一,主要介绍了常用的Builder(生成器)模式和模板模式。前者属于创建模式,后者属于行为模式。本文会结合具体的代码和实际的应用的案例进行分析。
Builder(生成器)的介绍
生成器,听名字就是知道和对象的创建有关系,我们思考下面一个例子,假设我们设计了一个类有这样一个提点,他的属性域特别多,并且很大一部分是可选的,由于java不支持像python这样的可选参数,那么我们必须在构造参数上下功夫,最简单的想法就是利用java的多态机制,为各个数量参数写一个构造方法。代码如下:
public class ManyParameterClass{
private type para1;
private type para2;
...;
private type paran;
public ManyParameterClass(type para1) {
ManyParameterClass(para1,defaultValue,...,defalutValue);
}
public ManyParameterCalss(type para1, type para2) {
ManyParameterClass(para1,para2,defaultValue,..,defaultValue);
}
...;
public ManyParameterClass(type para1, type para2,...,type paran) {
this.para1 = para1;
this.para2 = para2;
...;
this.paran = paran;
}
public static void main(String[] args) {
//the client call
ManyParameterClass obj1 = new ManyParameterClass(value1);
ManyParameterClass obj2 = new ManyParameterClass(value1,value2);
}
}
通过上述代码我们可以看出上述代码有以下弊端,对于类的设计者来说,势必会造成代码的冗余,当然这个还是小事情,java代码本来就是长长长长:)。最主要的问题是客户端在调用的时候,根本不知道如何选择参数,还有就是,就算当时在写代码时候清除每个参数的具体含义,当过一段时间,或者别人维护代码时,又需要从新学习该类的使用。在这种情况下,生成器设计模式应运而生,你会发现,生成器仿佛是专门为了弥补java这门不具备关键词参数而生的。
生成器的使用和实现
废话不多说,我们先来看几个在实际中使用生成器模式的例子:
//Ruquest对象的创建
Request request = newRequest.Builder()
.url("http://localhost:8002/setWrongTraceId").post(body).build();
//httpClient的创建
OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder()
.connectTimeout(50L,TimeUnit.SECONDS)
.readTimeout(60L, TimeUnit.SECONDS).build();
通过上述写法,我们可以很清楚的知道客户端在创建对象的时候进行了什么样的初始化,即使我们对上述类并不是很清楚。可以看到,生成器模拟了像python,ruby这样语言的关键词参数,同时还提供的给强大的参数检查功能。
下面我们通过一个具体的例子来看看具体是如何实现的,假设我们有这样的一个营养配方,配方表十分的复杂,下面我们看如何使用Builder模式的具体应用:
public class NutritionFacts{
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder{
private final int servingSize;
private final int servings;
private int calories = 0;
private int fat = 0;
private int carbohyrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calaries(int value) {
calorires = value;
return this;
}
public Builder fat(int value) {
fat = value;
return this;
}
public Builder carbohyrate(int value) {
carbohyrate = value;
return this;
}
public Builder sodium(int value) {
sodium = value;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
public NutritionFacts(Builder builder) {
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.carbohyrate = builder.carbohyrate;
this.sodium = builder.sodium;
}
public static void main(String[] args) {
NutritionFacts cola = new NutirtionFacts.Builder(200,8)
.calories(1000).sodium(35).build();
}
}
可以看到,build就像个构造器一样,可以对其参数加约束条件,这样就使得代码更加鲁棒,当违背了约束条件时,我们就可以抛出相应的异常来处理。
模板方法及其应用
模板方法是行为模式中的一种,他的主要目的是为了接口规范,其实在java公共库中大量的使用了这种模式,也是面向对象中的常用的模式。简单来讲就是在抽象类中定义公共模板方法,而在具体的实现子类中再定制自己的实现方法,当然这是通过重写父类的方法来实现的。
由于比较简答我们就不举例子了,而是说一下具体的应用。
java.io.InputStream
、java.io.OutputStream
、java.io.Reader
、java.io.Writer
的所有非抽象方法都是模板方法。
java.util.AbstracList
、java.util.AbstractSet
、java.util.AbstractMap
中的所有非抽象方法。
通过这样的设计,我们可以简化客户端的调用,因为这样相当于实现了多态,所以客户端不用为不同的参数都实例化不同的类,而是根据模板方法自动的加载对应的方法。
参考:
effective java 2ed p9:遇到多个构造器参数时要考虑构建器