前言:关于传递参数,当参数过多的时候我们可以考虑使用建造者模式。
#没用 Builder模式 之前是这样传参的:
如下所示,构造方法里面的参数一大堆,看起来就非常的混乱。
-
用了Builder模式之后是这样的
新建一个静态内部类Buider,通过它来构建参数,然后返回一个新的对象,最后在新的对象内部把值赋给当前类的成员变量,如下图:
-
可以看到改造后的代码,只存在一个Buider静态内部类,瞬间感觉清晰了不少。
-
附加一个简单的demo:
-
class User { // 下面是“一堆”的属性 private String name; private String password; private String nickName; private int age; // 构造方法私有化,不然客户端就会直接调用构造方法了 private User(String name, String password, String nickName, int age) { this.name = name; this.password = password; this.nickName = nickName; this.age = age; } // 静态方法,用于生成一个 Builder,这个不一定要有,不过写这个方法是一个很好的习惯, // 有些代码要求别人写 new User.UserBuilder().a()...build() 看上去就没那么好 public static UserBuilder builder() { return new UserBuilder(); } public static class UserBuilder { // 下面是和 User 一模一样的一堆属性 private String name; private String password; private String nickName; private int age; private UserBuilder() { } // 链式调用设置各个属性值,返回 this,即 UserBuilder public UserBuilder name(String name) { this.name = name; return this; } public UserBuilder password(String password) { this.password = password; return this; } public UserBuilder nickName(String nickName) { this.nickName = nickName; return this; } public UserBuilder age(int age) { this.age = age; return this; } // build() 方法负责将 UserBuilder 中设置好的属性“复制”到 User 中。 // 当然,可以在 “复制” 之前做点检验 public User build() { if (name == null || password == null) { throw new RuntimeException("用户名和密码必填"); } if (age <= 0 || age >= 150) { throw new RuntimeException("年龄不合法"); } // 还可以做赋予”默认值“的功能 if (nickName == null) { nickName = name; } return new User(name, password, nickName, age); } } }
-
核心是:先把所有的属性都设置给 Builder,然后 build() 方法的时候,将这些属性复制给实际产生的对象。 看看客户端的调用: public class APP { public static void main(String[] args) { User d = User.builder() .name("foo") .password("pAss12345") .age(25) .build(); } }
-
2018年1月19日 10:20:12 更新
附加一个swagger中看到的建造者模式:
/* * * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * */ package springfox.documentation.builders; import com.fasterxml.classmate.ResolvedType; import com.google.common.base.Optional; import springfox.documentation.schema.ModelReference; import springfox.documentation.service.AllowableValues; import springfox.documentation.service.Parameter; import springfox.documentation.service.VendorExtension; import java.util.List; import static com.google.common.collect.Lists.newArrayList; import static springfox.documentation.builders.BuilderDefaults.*; public class ParameterBuilder { private String name; private String description; private String defaultValue; private boolean required; private boolean allowMultiple; private AllowableValues allowableValues; private String paramType; private String paramAccess; private ResolvedType type; private ModelReference modelRef; private boolean hidden; private List<VendorExtension> vendorExtensions = newArrayList(); /** * Copy builder * * @param other parameter to copy from * @return this */ ParameterBuilder from(Parameter other) { return name(other.getName()) .allowableValues(other.getAllowableValues()) .allowMultiple(other.isAllowMultiple()) .defaultValue(other.getDefaultValue()) .description(other.getDescription()) .modelRef(other.getModelRef()) .parameterAccess(other.getParamAccess()) .parameterType(other.getParamType()) .required(other.isRequired()) .type(other.getType().orNull()) .hidden(other.isHidden()) .vendorExtensions(other.getVendorExtentions()); } /** * Updates the parameter name * * @param name - name of the parameter * @return this */ public ParameterBuilder name(String name) { this.name = defaultIfAbsent(name, this.name); return this; } /** * Updates the description of the parameter * * @param description - description * @return this */ public ParameterBuilder description(String description) { this.description = defaultIfAbsent(description, this.description); return this; } /** * Updates the default value of the parametr * * @param defaultValue - default value * @return this */ public ParameterBuilder defaultValue(String defaultValue) { this.defaultValue = defaultIfAbsent(defaultValue, this.defaultValue); return this; } /** * Updates if the parameter is required or optional * * @param required - flag to indicate if the parameter is required * @return this */ public ParameterBuilder required(boolean required) { this.required = required; return this; } /** * Updates if the parameter should allow multiple values * * @param allowMultiple - flag to indicate if the parameter supports multi-value * @return this */ public ParameterBuilder allowMultiple(boolean allowMultiple) { this.allowMultiple = allowMultiple; return this; } /** * Updates if the parameter is bound by a range of values or a range of numerical values * * @param allowableValues - allowable values (instance of @see springfox.documentation.service.AllowableListValues * or @see springfox.documentation.service.AllowableRangeValues) * @return */ public ParameterBuilder allowableValues(AllowableValues allowableValues) { this.allowableValues = emptyToNull(allowableValues, this.allowableValues); return this; } /** * Updates the type of parameter * * @param paramType - Could be header, cookie, body, query etc. * @return this */ public ParameterBuilder parameterType(String paramType) { this.paramType = defaultIfAbsent(paramType, this.paramType); return this; } /** * Updates the parameter access * * @param paramAccess - parameter access * @return this */ public ParameterBuilder parameterAccess(String paramAccess) { this.paramAccess = defaultIfAbsent(paramAccess, this.paramAccess); return this; } /** * Updates the type of parameter * * @param type - represents the resolved type of the parameter * @return this */ public ParameterBuilder type(ResolvedType type) { this.type = defaultIfAbsent(type, this.type); return this; } /** * Represents the convenience method to infer the model reference * Consolidate or figure out whats can be rolled into the other. * * @param modelRef * @return */ public ParameterBuilder modelRef(ModelReference modelRef) { this.modelRef = defaultIfAbsent(modelRef, this.modelRef); return this; } /** * Updates if the parameter is hidden * * @param hidden - flag to indicate if the parameter is hidden * @return this */ public ParameterBuilder hidden(boolean hidden) { this.hidden = hidden; return this; } /** * Updates the parameter extensions * * @param extensions - parameter extensions * @return this */ public ParameterBuilder vendorExtensions(List<VendorExtension> extensions) { this.vendorExtensions.addAll(nullToEmptyList(extensions)); return this; } public Parameter build() { return new Parameter( name, description, defaultValue, required, allowMultiple, modelRef, Optional.fromNullable(type), allowableValues, paramType, paramAccess, hidden, vendorExtensions); } }
总结:
相比只通过一个构造器创建实例,JavaBean模式的实例的构造过程被分成了好几个过程。
我们完全有可能在属性不完整的情况下使用这个实例。
当然,Builder也有缺点。
缺点1.创建实例前都要创建一个Builder实例。
缺点2.Builder模式编写起来较为冗长。
但是,当构建一个实例需要很多步骤(或者很多让人混淆的参数)的时候,Builder模式是个不错的选择。