前言:關於傳遞參數,當參數過多的時候我們可以考慮使用建造者模式。
#沒用 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模式是個不錯的選擇。