构建者(Builder)设计模式(又叫生成器设计模式):
当一个类的内部数据过于复杂的时候(通常是负责持有数据的类,比如Config、VO、PO、Entity…),要创建的话可能就需要了解这个类的内部结构,还有这些东西是怎么组织装配等一大坨乱七八糟的东西,这个时候就会增加学习成本而且会很混乱,这个时候就想啊想一种什么法子来管理一下这个类中的数据呢,怎么在创建的时候让它按部就班的来,并且代码可读性很好别让我看花了眼啊,我要的东西也能都很好设置进来,这就是Builder模式的应用场景,Builder模式可以将一个类的构建和表示进行分离。
看一个例子:
public class Student { private int id; private String name; private String passwd; private String sex; private String address; // 构造器尽量缩小范围 private Student() { } // 构造器尽量缩小范围 private Student(Student origin) { // 拷贝一份 this.id = origin.id; this.name = origin.name; this.passwd = origin.passwd; this.sex = origin.sex; this.address = origin.address; } public int getId() { return id; } public String getName() { return name; } public String getPasswd() { return passwd; } public String getSex() { return sex; } public String getAddress() { return address; } /** * Student的创建完全依靠Student.Builder,使用一种方法链的方式来创建 * */ public static class Builder { private Student target; public Builder() { target = new Student(); } public Builder address(int id) { target.id = id; return this; } public Builder name(String name) { target.name = name; return this; } public Builder password(String passwd) { target.passwd = passwd; return this; } public Builder sex(String sex) { target.sex = sex; return this; } public Builder address(String address) { target.address = address; return this; } public Student build() { return new Student(target); } } }
Student并不是直接new出来的,对其构造器进行了处理使其可访问范围尽可能的小,只让它通过Student.Builder来构建自己,在Student.Builder中提供了一种类set的方法链的方式来设置值,然后在最后的build()方法的时候会返回一个Student对象,现在要创建一个Student对象,代码如下:
Student s=new Student.Builder().name("CC").password("qwerty").sex("男").address("银河系第二旋臂").build();
再对比一下如果不使用构造者模式(一般情况下的用法):
/** * 学生实体 * @author CC11001100 * */ public class Student { private int id; private String name; private String passwd; private String sex; private String address; public Student() { } public Student(String name, String passwd, String sex, String address) { super(); this.name = name; this.passwd = passwd; this.sex = sex; this.address = address; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPasswd() { return passwd; } public void setPasswd(String passwd) { this.passwd = passwd; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
创建对象:
Student s=new Student("CC","qwerty","男","银河系第二旋臂");
对比一下进行一个优劣性分析:
一般的套路:优点是比较简单,开发效率高,缺点是如果参数真的很多的话鬼知道每个对应的是什么意思啊。
Builder模式:优点是可以将构造器的setter方法名取成类似注释的方式,这样我们可以很清晰的知道刚才究竟设置的什么值,可读性较高,缺点是比较冗长。
总结:初步的理解Builder模式解决了要设置的参数过多不好管理的问题(感觉每次构建不同对象是废话 – -)。
从Struts2框架中拿出来的两个Builder模式的例子(都是Config类):
ActionConfig :
public class ActionConfig extends Located implements Serializable { public static final String WILDCARD = "*"; protected List<InterceptorMapping> interceptors; // a list of interceptorMapping Objects eg. List<InterceptorMapping> protected Map<String,String> params; protected Map<String, ResultConfig> results; protected List<ExceptionMappingConfig> exceptionMappings; protected String className; protected String methodName; protected String packageName; protected String name; protected Set<String> allowedMethods; protected ActionConfig(String packageName, String name, String className) { this.packageName = packageName; this.name = name; this.className = className; params = new LinkedHashMap<String, String>(); results = new LinkedHashMap<String, ResultConfig>(); interceptors = new ArrayList<InterceptorMapping>(); exceptionMappings = new ArrayList<ExceptionMappingConfig>(); allowedMethods = new HashSet<String>(); allowedMethods.add(WILDCARD); } /** * Clones an ActionConfig, copying data into new maps and lists * @param orig The ActionConfig to clone * @Since 2.1 */ protected ActionConfig(ActionConfig orig) { this.name = orig.name; this.className = orig.className; this.methodName = orig.methodName; this.packageName = orig.packageName; this.params = new LinkedHashMap<String,String>(orig.params); this.interceptors = new ArrayList<InterceptorMapping>(orig.interceptors); this.results = new LinkedHashMap<String,ResultConfig>(orig.results); this.exceptionMappings = new ArrayList<ExceptionMappingConfig>(orig.exceptionMappings); this.allowedMethods = new HashSet<String>(orig.allowedMethods); } public String getName() { return name; } public String getClassName() { return className; } public List<ExceptionMappingConfig> getExceptionMappings() { return exceptionMappings; } public List<InterceptorMapping> getInterceptors() { return interceptors; } public Set<String> getAllowedMethods() { return allowedMethods; } /** * Returns name of the action method * * @return name of the method to execute */ public String getMethodName() { return methodName; } /** * @return Returns the packageName. */ public String getPackageName() { return packageName; } public Map<String, String> getParams() { return params; } public Map<String, ResultConfig> getResults() { return results; } public boolean isAllowedMethod(String method) { if (allowedMethods.size() == 1 && WILDCARD.equals(allowedMethods.iterator().next())) { return true; } else { return allowedMethods.contains(method); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ActionConfig)) { return false; } final ActionConfig actionConfig = (ActionConfig) o; if ((className != null) ? (!className.equals(actionConfig.className)) : (actionConfig.className != null)) { return false; } if ((name != null) ? (!name.equals(actionConfig.name)) : (actionConfig.name != null)) { return false; } if ((interceptors != null) ? (!interceptors.equals(actionConfig.interceptors)) : (actionConfig.interceptors != null)) { return false; } if ((methodName != null) ? (!methodName.equals(actionConfig.methodName)) : (actionConfig.methodName != null)) { return false; } if ((params != null) ? (!params.equals(actionConfig.params)) : (actionConfig.params != null)) { return false; } if ((results != null) ? (!results.equals(actionConfig.results)) : (actionConfig.results != null)) { return false; } if ((allowedMethods != null) ? (!allowedMethods.equals(actionConfig.allowedMethods)) : (actionConfig.allowedMethods != null)) { return false; } return true; } @Override public int hashCode() { int result; result = (interceptors != null ? interceptors.hashCode() : 0); result = 31 * result + (params != null ? params.hashCode() : 0); result = 31 * result + (results != null ? results.hashCode() : 0); result = 31 * result + (exceptionMappings != null ? exceptionMappings.hashCode() : 0); result = 31 * result + (className != null ? className.hashCode() : 0); result = 31 * result + (methodName != null ? methodName.hashCode() : 0); result = 31 * result + (packageName != null ? packageName.hashCode() : 0); result = 31 * result + (name != null ? name.hashCode() : 0); result = 31 * result + (allowedMethods != null ? allowedMethods.hashCode() : 0); return result; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{ActionConfig "); sb.append(name).append(" ("); sb.append(className); if (methodName != null) { sb.append(".").append(methodName).append("()"); } sb.append(")"); sb.append(" - ").append(location); sb.append("}"); return sb.toString(); } /** * The builder for this object. An instance of this object is the only way to construct a new instance. The * purpose is to enforce the immutability of the object. The methods are structured in a way to support chaining. * After setting any values you need, call the {@link #build()} method to create the object. */ public static class Builder implements InterceptorListHolder{ private ActionConfig target; public Builder(ActionConfig toClone) { target = new ActionConfig(toClone); } public Builder(String packageName, String name, String className) { target = new ActionConfig(packageName, name, className); } public Builder packageName(String name) { target.packageName = name; return this; } public Builder name(String name) { target.name = name; return this; } public Builder className(String name) { target.className = name; return this; } public Builder defaultClassName(String name) { if (StringUtils.isEmpty(target.className)) { target.className = name; } return this; } public Builder methodName(String method) { target.methodName = method; return this; } public Builder addExceptionMapping(ExceptionMappingConfig exceptionMapping) { target.exceptionMappings.add(exceptionMapping); return this; } public Builder addExceptionMappings(Collection<? extends ExceptionMappingConfig> mappings) { target.exceptionMappings.addAll(mappings); return this; } public Builder exceptionMappings(Collection<? extends ExceptionMappingConfig> mappings) { target.exceptionMappings.clear(); target.exceptionMappings.addAll(mappings); return this; } public Builder addInterceptor(InterceptorMapping interceptor) { target.interceptors.add(interceptor); return this; } public Builder addInterceptors(List<InterceptorMapping> interceptors) { target.interceptors.addAll(interceptors); return this; } public Builder interceptors(List<InterceptorMapping> interceptors) { target.interceptors.clear(); target.interceptors.addAll(interceptors); return this; } public Builder addParam(String name, String value) { target.params.put(name, value); return this; } public Builder addParams(Map<String,String> params) { target.params.putAll(params); return this; } public Builder addResultConfig(ResultConfig resultConfig) { target.results.put(resultConfig.getName(), resultConfig); return this; } public Builder addResultConfigs(Collection<ResultConfig> configs) { for (ResultConfig rc : configs) { target.results.put(rc.getName(), rc); } return this; } public Builder addResultConfigs(Map<String,ResultConfig> configs) { target.results.putAll(configs); return this; } public Builder addAllowedMethod(String methodName) { target.allowedMethods.add(methodName); return this; } public Builder addAllowedMethod(Collection<String> methods) { target.allowedMethods.addAll(methods); return this; } public Builder location(Location loc) { target.location = loc; return this; } public ActionConfig build() { target.params = Collections.unmodifiableMap(target.params); target.results = Collections.unmodifiableMap(target.results); target.interceptors = Collections.unmodifiableList(target.interceptors); target.exceptionMappings = Collections.unmodifiableList(target.exceptionMappings); target.allowedMethods = Collections.unmodifiableSet(target.allowedMethods); ActionConfig result = target; target = new ActionConfig(target); return result; } } }
ExceptionMappingConfig:
public class ExceptionMappingConfig extends Located implements Serializable { private String name; private String exceptionClassName; private String result; private Map<String,String> params; protected ExceptionMappingConfig(String name, String exceptionClassName, String result) { this.name = name; this.exceptionClassName = exceptionClassName; this.result = result; this.params = new LinkedHashMap<String,String>(); } protected ExceptionMappingConfig(ExceptionMappingConfig target) { this.name = target.name; this.exceptionClassName = target.exceptionClassName; this.result = target.result; this.params = new LinkedHashMap<String,String>(target.params); } public String getName() { return name; } public String getExceptionClassName() { return exceptionClassName; } public String getResult() { return result; } public Map<String,String> getParams() { return params; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ExceptionMappingConfig)) { return false; } final ExceptionMappingConfig exceptionMappingConfig = (ExceptionMappingConfig) o; if ((name != null) ? (!name.equals(exceptionMappingConfig.name)) : (exceptionMappingConfig.name != null)) { return false; } if ((exceptionClassName != null) ? (!exceptionClassName.equals(exceptionMappingConfig.exceptionClassName)) : (exceptionMappingConfig.exceptionClassName != null)) { return false; } if ((result != null) ? (!result.equals(exceptionMappingConfig.result)) : (exceptionMappingConfig.result != null)) { return false; } if ((params != null) ? (!params.equals(exceptionMappingConfig.params)) : (exceptionMappingConfig.params != null)) { return false; } return true; } @Override public int hashCode() { int hashCode; hashCode = ((name != null) ? name.hashCode() : 0); hashCode = (29 * hashCode) + ((exceptionClassName != null) ? exceptionClassName.hashCode() : 0); hashCode = (29 * hashCode) + ((result != null) ? result.hashCode() : 0); hashCode = (29 * hashCode) + ((params != null) ? params.hashCode() : 0); return hashCode; } /** * The builder for this object. An instance of this object is the only way to construct a new instance. The * purpose is to enforce the immutability of the object. The methods are structured in a way to support chaining. * After setting any values you need, call the {@link #build()} method to create the object. */ public static class Builder{ private ExceptionMappingConfig target; public Builder(ExceptionMappingConfig toClone) { target = new ExceptionMappingConfig(toClone); } public Builder(String name, String exceptionClassName, String result) { target = new ExceptionMappingConfig(name, exceptionClassName, result); } public Builder name(String name) { target.name = name; return this; } public Builder exceptionClassName(String name) { target.exceptionClassName = name; return this; } public Builder result(String result) { target.result = result; return this; } public Builder addParam(String name, String value) { target.params.put(name, value); return this; } public Builder addParams(Map<String,String> params) { target.params.putAll(params); return this; } public Builder location(Location loc) { target.location = loc; return this; } public ExceptionMappingConfig build() { target.params = Collections.unmodifiableMap(target.params); ExceptionMappingConfig result = target; target = new ExceptionMappingConfig(target); return result; } } }
参考资料: