設計模式系列之「建造者模式」

YoungManSter發表於2017-12-13

歡迎收看俗到掉渣的《小Y講堂》節目,大家好,我是小Y,一個集性感毛髮與才華於一身的程式猿!近日收到《魂鬥羅.歸來》中的肌肉男比爾·雷澤的投訴,說要投訴小Y最近冷落他,太久沒有讓他上節目show muscle。沒辦法,為了滿足這個悶騷的老男人,小Y把這次的主題設定為如果比爾是程式設計師,會怎麼用建造者模式來實現關卡武器裝配。oh,my God,很難想象戰鬥狂人叼著雪茄在死命敲程式碼的情形(一陣惡寒啊),得趕緊來幅小Y牌“止吐”圖來鎮鎮。

設計模式系列之「建造者模式」

一、初出茅廬的比爾·雷澤

比爾最近迷上了程式設計,剛學到點三腳貓功夫就吵著要寫段程式碼為自己代言,要把自己不同的形象展現出來,比爾寫了以下程式碼:

①角色的基配

public class Character {
	//赤裸裸的比爾雷澤,出戰前需要做充能、裝備主武器以及副武器
	private Energy energy;
	private MainWeapon mainWeapon;
	private ViceWeapon viceWeapon;

	public Character(Energy energy, MainWeapon mainWeapon, ViceWeapon viceWeapon) {
		this.energy = energy;
		this.mainWeapon = mainWeapon;
		this.viceWeapon = viceWeapon;
	}

	@Override
	public String toString() {
		return energy.getEnergy()+mainWeapon.getMainWeapon()+viceWeapon.getViceWeapon();
	}
}
複製程式碼

②出戰前基配型別(充能、選擇主武器以及副武器)

//充能
public abstract class Energy {
	public abstract String getEnergy();
}

//主武器
public abstract class MainWeapon {
	public abstract String getMainWeapon();
}

//副武器
public abstract class ViceWeapon {
	public abstract String getViceWeapon();
}
複製程式碼

③1VS1的武器裝配

//1VS1的充能
public class OneVsOneEnergy extends Energy{
	@Override
	public String getEnergy() {
		return "充能完成。";
	}
}

//1VS1的主武器裝配
public class OneVsOneMainWeapon extends MainWeapon{

	@Override
	public String getMainWeapon() {
		return "主武器:黃金加特林。";
	}
}

//1VS1的副武器裝配
public class OneVsOneViceWeapon extends ViceWeapon{
	@Override
	public String getViceWeapon() {
		return "副武器:集速手雷。";
	}
}
複製程式碼

④3VS3的武器裝配

//3VS3的充能
public class ThreeVsThreeEnergy extends Energy{
	@Override
	public String getEnergy() {
		return "充能完成。";
	}
}

//3VS3的主武器裝配
public class ThreeVsThreeMainWeapon extends MainWeapon{

	@Override
	public String getMainWeapon() {
		return "主武器:突擊步槍。";
	}
}

//3VS3的副武器裝配
public class ThreeVsThreeViceWeapon extends ViceWeapon{
	@Override
	public String getViceWeapon() {
		return "副武器:等離子噴射器。";
	}
}
複製程式碼

⑤Client實現

public class Client {
	public static void main(String[] args){
		//1VS1下的比爾
		Character OneVsOneOfBill=new Character(new OneVsOneEnergy(),new OneVsOneMainWeapon(),new OneVsOneViceWeapon());
		System.out.println(OneVsOneOfBill);
		//3VS3下的比爾
		Character threeVThreeOfBill=new Character(new ThreeVsThreeEnergy(),new ThreeVsThreeMainWeapon(),new ThreeVsThreeViceWeapon());
		System.out.println(threeVThreeOfBill);
	
	}
}
複製程式碼

輸出結果

充能完成。主武器:黃金加特林。副武器:集速手雷。 
充能完成。主武器:突擊步槍。副武器:等離子噴射器。 
複製程式碼

對於一個剛學習程式設計的比爾來說,撇開設計模式來說,能夠寫出這樣的程式碼,小Y都是佩服得不要不要的了,但是為了唬住這個沒長全毛的比爾,小Y毅然搬出了設計模式,對比爾進行了義正言辭的批評教育:

  • 隨著等級的越來越高,面對的關卡種類就會越來越多,這也意味著不同的關卡的主副武器的搭配的種類就會越來越多,況且這個例子只是一個簡單的傳參,如果傳參複雜點還按照這種傳參的方式進行會很容易搞混,出現搭配不對的情況。
  • 產品的內部組成暴露給客戶端,封裝性差。

為了防止比爾反駁,小Y立馬丟擲建造者模式

二、基本概念

1.定義

將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。

2.理解

就是把一個產品(物件)表示(展示)和構建(建立)過程分離開來,這樣產品的構建流程相同卻可以有不同的產品表示。

3.為何使用建造者模式
  • 是為了將構建複雜物件的過程和它的部件解耦。
  • 建造者模式的封裝性很好。使用建造者模式可以有效的封裝變化。
4.應用場景
  • 同一個建立過程需要有不同的內部表象的產品物件。
  • 建立複雜物件的演算法獨立於組成物件的部件。
5.角色介紹

設計模式系列之「建造者模式」

  • Director導演類
    負責呼叫適當的建造者來組建產品,導演類一般不與產品類發生依賴關係,與導演類直接互動的是建造者類。

  • Builder抽象建造者
    規範產品的組建,一般是由子類實現。

  • ConcreteBuilder具體建造者
    實現抽象類的所有未實現的方法,具體來說一般是兩項任務:組建產品;返回組建好的產品。。

  • Product產品類
    由一系列部件組成,一般是一個較為複雜的物件,也就是說建立物件的過程比較複雜,一般會有比較多的程式碼量。

  • 建造模式分成兩個很重要的部分:
    一個部分是Builder介面,這裡是定義瞭如何構建各個部件,也就是知道每個部件功能如何實現,以及如何裝配這些部件到產品中去;另外一個部分是Director,Director是知道如何組合來構建產品,也就是說Director負責整體的構建演算法,而且通常是分步驟地來執行。  

三、案列實現

經過小Y孜孜不倦的教誨,比爾·雷澤總算是領悟了建造者模式的精髓,決定了重新修改了修改一下上面的程式碼,經過整理得到:

1.UML清單

設計模式系列之「建造者模式」

2.程式碼實現

修改後角色的基配

public class Character {
	//赤裸裸的比爾雷澤,出戰前需要做充能、裝備主武器以及副武器
	private Energy energy;
	private MainWeapon mainWeapon;
	private ViceWeapon viceWeapon;

	public Energy getEnergy() {
		return energy;
	}

	public void setEnergy(Energy energy) {
		this.energy = energy;
	}

	public MainWeapon getMainWeapon() {
		return mainWeapon;
	}

	public void setMainWeapon(MainWeapon mainWeapon) {
		this.mainWeapon = mainWeapon;
	}

	public ViceWeapon getViceWeapon() {
		return viceWeapon;
	}

	public void setViceWeapon(ViceWeapon viceWeapon) {
		this.viceWeapon = viceWeapon;
	}

	@Override
	public String toString() {
		return energy.getEnergy()+mainWeapon.getMainWeapon()+viceWeapon.getViceWeapon();
	}
}
複製程式碼

②角色建造抽象類

public interface CharacterBuilder {
	void makeEnergy();
	void makeMainWeapon();
	void makeViceWeapon();

	public Character build();
}
複製程式碼

③1VS1具體建造者

public class OneVsOneBulider implements CharacterBuilder {

	private Character character;

	public OneVsOneBulider() {
		this.character = new Character();
	}

	@Override
	public void makeEnergy() {
		character.setEnergy(new OneVsOneEnergy());
	}

	@Override
	public void makeMainWeapon() {
		character.setMainWeapon(new OneVsOneMainWeapon());
	}

	@Override
	public void makeViceWeapon() {
		character.setViceWeapon(new OneVsOneViceWeapon());
	}

	@Override
	public Character build() {
		return character;
	}
}
複製程式碼

④3VS3具體建造者

public class ThreeVsThreeBulider implements CharacterBuilder {

	private Character character;

	public ThreeVsThreeBulider() {
		character = new Character();
	}

	@Override
	public void makeEnergy() {
		character.setEnergy(new ThreeVsThreeEnergy());
	}

	@Override
	public void makeMainWeapon() {
		character.setMainWeapon(new ThreeVsThreeMainWeapon());
	}

	@Override
	public void makeViceWeapon() {
		character.setViceWeapon(new ThreeVsThreeViceWeapon());
	}

	@Override
	public Character build() {
	return character;
	}
}
複製程式碼

⑤Director導演類

public class CharacterDirector {

	private CharacterBuilder characterBuilder;

	public CharacterDirector(CharacterBuilder characterBuilder) {
		this.characterBuilder = characterBuilder;
	}

	public Character createCharacter(){
		characterBuilder.makeEnergy();
		characterBuilder.makeMainWeapon();
		characterBuilder.makeViceWeapon();
		return characterBuilder.build();
	}
}
複製程式碼

⑥Client實現

public class Client {

	public static void main(String[] args){
		//1VS1下的比爾
		CharacterBuilder oneVsOneBulider=new OneVsOneBulider();
		CharacterDirector characterDirector=new CharacterDirector(oneVsOneBulider);
		System.out.println(characterDirector.createCharacter());
		//3VS3下的比爾
		CharacterBuilder threeVsThreeBulider=new ThreeVsThreeBulider();
		characterDirector=new CharacterDirector(threeVsThreeBulider);
		System.out.println(characterDirector.createCharacter());

	}
}
複製程式碼

輸出結果

充能完成。主武器:黃金加特林。副武器:集速手雷。 
充能完成。主武器:突擊步槍。副武器:等離子噴射器
複製程式碼

四、優缺點

1.優點

  • 封裝性,使用建造者模式可以使客戶端不必知道產品內部組成的細節,如例子中我們就不需要關心每一個具體的模型內部是如何實現。

  • 建造者獨立,容易擴充套件。OneVsOneBulider和ThreeVsThreeBulider是相互獨立的,對系統的擴充套件非常有利。

  • 便於控制細節風險。由於具體的建造者是獨立的,因此可以對建造過程逐步細化,而不對其他的模組產生任何影響。

  • 增加新的具體建造者無須修改原有類庫的程式碼,指揮者類針對抽象建造者類程式設計,系統擴充套件方便,符合“開閉原則”。

2.缺點

  • 建造者模式所建立的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用範圍受到一定的限制。

  • 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大。

五、總結

建造者模式關注的是零件型別和裝配工藝(順序),這是它與工廠方法模式最大不同的地方,雖然同為建立類模式,但是注重點不同。下一篇就是工廠方法模式,有興趣的可以繼續留意。

節目到了尾聲了,讓我們用熱烈的掌聲感謝重量級嘉賓比爾雷澤,好走不送。

Android技術交流吧

相關文章