全篇干貨芙委,值得你用幾分鐘認真讀完蠢护。
先看定義:將一個復雜的構建與其表示想分離,使得同樣的構建過程可以創(chuàng)建不同的標示唆迁。
使用場景:當一個類的構造函數參數個數超過4個,而且這些參數有些是可選的參數竞穷,考慮使用構造者模式唐责。
例子
在App中用戶會分成vip和普通用戶,vip用戶會有app的圖標和用戶專屬圖標瘾带。這里假設用戶有id和name(昵稱)是必選的鼠哥,vipAppLogo(vip的app圖標)和vipUserLogo(vip用戶圖標)是可選的,vipAppLogo和vipUserLogo也可能有其中一個(vip可能只開啟了一個)看政,最原始的方案大概是這樣的:
class User {
var id: Int;
var name: String;
var vipAppLogo: Image; // 圖標存在本地的資源文件中朴恳,通過Resource.getVipAppLogo()獲取
var vipUserLogo: Image; // 圖標存在本地資源文件中,通過Resource.getVipUserLogo()獲取
func init(id: Int, name: String) {
self.id = id;
self.name = name;
}
func init(id: Int, name: String, vipAppLogo: Image) {
self.id = id;
self.name = name;
self.vipAppLogo = vipAppLogo;
}
func init(id: Int, name: String, vipUserLogo: Image) {
self.id = id;
self.name = name;
self.vipUserLogo = vipUserLogo;
}
func init(id: Int, name: String, vipAppLogo: Image, vipUserLogo: Image) {
self.id = id;
self.name = name;
self.vipAppLogo = vipAppLogo;
self.vipUserLogo = vipUserLogo;
}
}
使用這種方法在創(chuàng)建User的時候需要決定到底該使用哪個初始化方法允蚣,而且閱讀起來及其不便于颖,也非常不利于擴展,可以設想一下某次需求變化給User增加一個age的必傳參數厉萝。恍飘。榨崩。相信這種情況都遇到過。
讀到這里章母,聰明的你可能會想到母蛛,既然有的參數是可選的,那么我是否可以只使用一個初始化函數乳怎,而把其他參數都使用get彩郊、set方法傳遞呢,就像下面這樣蚪缀。
class User {
var id: Int;
var name: Int;
var vipAppLogo: Image;
var vipUserLogo: Image;
func init(id: Int, name: String) {
self.id = id;
self.name = name;
}
func setVipAppLogo(vipAppLogo: Image) {
self.avatarImage = avatarImage;
}
func setVipUserLogo(vipUserLogo: Image) {
self.vipUserLogo = vipUserLogo;
}
}
不得不說這種方法確實解決了上面提高的問題秫逝,但是如果在調用setNo之前就使用了no的話,是不是就取不到值了呢询枚?而且违帆,對外直接暴露set方法的話,調用者隨時都可以修改屬性值金蜀,對內部來說是很不安全的刷后。
現在使用構建者模式可以這樣做:
class User {
var id: int;
var name: string;
var vipAppLogo: Image;
var vipUserLogo: Image;
func init(id: int, name: string) {
self.id = id;
self.name = name;
}
func setVipAppLogo(vipAppLogo: Image) {
self.vipAppLogo = vipAppLogo;
}
func setVipUserLogo(vipUserLogo: Image) {
self.vipUserLogo = vipUserLogo;
}
}
interface UserBuilder {
func setVipAppLogo(vipAppLogo: Image);
func setVipUserLogo(vipUserLogo: Image);
func init(id: int, name: string);
func getUser() -> User;
}
class ConcreteUserBuilder: UserBuilder {
private var someUser: User;
func init(id: int, name: string) {
someUser = User(id, name);
}
func setVipAppLogo(vipAppLogo: Image) {
someUser.no = no;
}
func setVipUserLogo(vipUserLogo: Image) {
someUser.vipUserLogo = vipUserLogo;
}
func getUser() -> User {
return self.someUser;
}
}
class UserDirector {
func createUser(build: UserBuilder, vipLevel: int) -> User {
if (vipLevel > 10) {
build.setVipAppLogo(Resource.getVipAppLogo(vipLevel);
build.setVipUserLogo(Resource.getUserAppLogo(vipLevel);
}
return build.getUser();
}
}
使用的時候:
let xiaomingBuilder: ConcreteUserBuilder = new ConcreteUserBuilder(id: 1, name: "xiaoming");
let director: UserDirector = new UserDirector();
let xiaoming = director.createUser(build: xiaomingBuilder, vipLevel: 20);
經過建造者模式的封裝,User的創(chuàng)建過程被掩蓋起來渊抄。尤其是在需求變動的時候優(yōu)勢尤為顯著尝胆。比如在vipLevel大于15時才有vipAppLogo,而在vipLevel大于20時才有vipUserLogo护桦,這時僅改動Builder里面的代碼就可以完成這個需求含衔。
下面提供一種簡化的使用方式,可能在某些語言里不適用二庵。
public class Computer {
private final String cpu;//必須
private final String ram;//必須
private final int usbCount;//可選
private final String keyboard;//可選
private final String display;//可選
private Computer(Builder builder){
this.cpu=builder.cpu;
this.ram=builder.ram;
this.usbCount=builder.usbCount;
this.keyboard=builder.keyboard;
this.display=builder.display;
}
public static class Builder{
private String cpu;//必須
private String ram;//必須
private int usbCount;//可選
private String keyboard;//可選
private String display;//可選
public Builder(String cup,String ram){
this.cpu=cup;
this.ram=ram;
}
public Builder setUsbCount(int usbCount) {
this.usbCount = usbCount;
return this;
}
public Builder setKeyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public Builder setDisplay(String display) {
this.display = display;
return this;
}
public Computer build(){
return new Computer(this);
}
}
}
當一個類的構造函數參數個數超過4個贪染,而且這些參數有些是可選的參數,考慮使用構造者模式眨猎。
本文到這里就結束了抑进,感謝閱讀。有不對之處請指正睡陪。