Java設計模式----建造者模式(Builder)

林花謝了春紅發表於2016-12-11

1.什麼是建造者模式

無論是在現實世界中還是在軟體系統中,都存在一些複雜的物件,它們擁有多個組成部分,如汽車,它包括車輪、方向盤、傳送機等各種部件。而對於大多數使用者而言,無須知道這些部件的裝配細節,也幾乎不會使用單獨某個部件,而是使用一輛完整的汽車,可以通過建造者模式對其進行設計與描述,建造者模式可以將部件和其組裝過程分開,一步一步建立一個複雜的物件。使用者只需要指定複雜物件的型別就可以得到該物件,而無須知道其內部的具體構造細節。

在軟體開發中,也存在大量類似汽車一樣的複雜物件,它們擁有一系列成員屬性,這些成員屬性中有些是引用型別的成員物件。而且在這些複雜物件中,還可能存在一些限制條件,如某些屬性沒有賦值則複雜物件不能作為一個完整的產品使用;有些屬性的賦值必須按照某個順序,一個屬性沒有賦值之前,另一個屬性可能無法賦值等。

複雜物件相當於一輛有待建造的汽車,而物件的屬性相當於汽車的部件,建造產品的過程就相當於組合部件的過程。由於組合部件的過程很複雜,因此,這些部件的組合過程往往被“外部化”到一個稱作建造者的物件裡,建造者返還給客戶端的是一個已經建造完畢的完整產品物件,而使用者無須關心該物件所包含的屬性以及它們的組裝方式,這就是建造者模式的模式動機。

2.實現方式

例子:
    Sunny軟體公司欲開發一個視訊播放軟體,為了給使用者使用提供方便,
    該播放軟體提供多種介面顯示模式,如完整模式、精簡模式、記憶模式、網路模式等。
    在不同的顯示模式下主介面的組成元素有所差異,如在完整模式下將顯示選單、播放列表、
    主視窗、控制條等,在精簡模式下只顯示主視窗和控制條,而在記憶模式下將顯示主視窗、
    控制條、收藏列表等。嘗試使用建造者模式設計該軟體。
分析:
    介面為產品類,介面顯示模式為建造者類
實現模式:
    建造者模式(不要使用橋接模式哦,看似有兩個維度的變化,其實沒有)

Player.java
package cn.limbo.design_patterns.builder;

/**
 * 產品類
 * Created by limbo on 2016/12/10.
 */
public class Player {

	//實際上這些型別都是有實體類的,為了簡單起見,全都使用了String型別
	private String menu; //選單
	private String playList; //播放列表
	private String mainWindow; //主視窗
	private String controller; // 控制條
	private String keepList; // 收藏列表

	public Player() {
	}

	public String getMenu() {
		return menu;
	}

	public void setMenu(String menu) {
		this.menu = menu;
	}

	public String getPlayList() {
		return playList;
	}

	public void setPlayList(String playList) {
		this.playList = playList;
	}

	public String getMainWindow() {
		return mainWindow;
	}

	public void setMainWindow(String mainWindow) {
		this.mainWindow = mainWindow;
	}

	public String getController() {
		return controller;
	}

	public void setController(String controller) {
		this.controller = controller;
	}

	public String getKeepList() {
		return keepList;
	}

	public void setKeepList(String keepList) {
		this.keepList = keepList;
	}

	@Override
	public String toString() {
		return "Player{" +
				"menu='" + menu + '\'' +
				", playList='" + playList + '\'' +
				", mainWindow='" + mainWindow + '\'' +
				", controller='" + controller + '\'' +
				", keepList='" + keepList + '\'' +
				'}';
	}
}
PlayerBuilder.java
package cn.limbo.design_patterns.builder;

/**
 * 產品類建造者
 * 抽象類
 * Created by limbo on 2016/12/10.
 */
public abstract class PlayerBuilder {

	//方便子類進行訪問
	protected Player player = new Player();

	//產品的基礎的構件
	public abstract void buildMainWindow();

	public abstract void buildController();

	//產品的擴充套件構件
	//提供一個預設實現
	public void buildMenu() {
		this.player.setMenu(null);
	}


	public void buildPlayList() {
		this.player.setPlayList(null);
	}


	public void buildKeepList() {
		this.player.setKeepList(null);
	}


//	//進行細粒度的控制,決定是否存在相應的物件,一般用於擴充套件功能
//	//指揮者進行呼叫,決定是否建造。
//	//擴充的子類進行覆寫,預設都是沒有擴充套件功能的
//	public boolean hasMenu() {
//		return false;
//	}
//
//	public boolean hasPlayList() {
//		return false;
//	}
//
//	public boolean hasKeepList() {
//		return false;
//	}

	//建立並返回一個建造完成的物件
	public Player build() {

		//每個player都有的東西先建造
		this.buildMainWindow();
		this.buildController();

//		//如果有選單則建立選單
//		if(this.hasMenu()){
//			this.buildMenu();
//		}
//
//		if(this.hasPlayList()){
//			this.buildPlayList();
//		}
//
//		if(this.hasKeepList()){
//			this.buildKeepList();
//		}
		//產品的擴充套件功能
		this.buildMenu();
		this.buildPlayList();
		this.buildKeepList();

		return this.player;

	}

}

FullModeBuilder.java
package cn.limbo.design_patterns.builder.concrete_builder;

import cn.limbo.design_patterns.builder.PlayerBuilder;

/**
 * 完整模式
 * Created by limbo on 2016/12/10.
 */
public class FullModeBuilder extends PlayerBuilder {

	@Override
	public void buildMainWindow() {
		this.player.setMainWindow("完整模式主視窗");
	}

	@Override
	public void buildController() {
		this.player.setController("完整模式控制條");
	}

	@Override
	public void buildMenu() {
		this.player.setMenu("完整模式選單");
	}

	@Override
	public void buildPlayList() {
		this.player.setPlayList("完整模式播放列表");
	}

	@Override
	public void buildKeepList() {
		this.player.setKeepList("完整模式收藏列表");
	}
}

KeepModeBuilder.java

package cn.limbo.design_patterns.builder.concrete_builder;

import cn.limbo.design_patterns.builder.PlayerBuilder;

/**
 * 完整模式
 * Created by limbo on 2016/12/10.
 */
public class FullModeBuilder extends PlayerBuilder {

	@Override
	public void buildMainWindow() {
		this.player.setMainWindow("完整模式主視窗");
	}

	@Override
	public void buildController() {
		this.player.setController("完整模式控制條");
	}

	@Override
	public void buildMenu() {
		this.player.setMenu("完整模式選單");
	}

	@Override
	public void buildPlayList() {
		this.player.setPlayList("完整模式播放列表");
	}

	@Override
	public void buildKeepList() {
		this.player.setKeepList("完整模式收藏列表");
	}
}

SimplifyModeBuilder.java
package cn.limbo.design_patterns.builder.concrete_builder;

import cn.limbo.design_patterns.builder.PlayerBuilder;

/**
 * 完整模式
 * Created by limbo on 2016/12/10.
 */
public class FullModeBuilder extends PlayerBuilder {

	@Override
	public void buildMainWindow() {
		this.player.setMainWindow("完整模式主視窗");
	}

	@Override
	public void buildController() {
		this.player.setController("完整模式控制條");
	}

	@Override
	public void buildMenu() {
		this.player.setMenu("完整模式選單");
	}

	@Override
	public void buildPlayList() {
		this.player.setPlayList("完整模式播放列表");
	}

	@Override
	public void buildKeepList() {
		this.player.setKeepList("完整模式收藏列表");
	}
}

測試類:
package cn.limbo.test;

import cn.limbo.design_patterns.builder.Player;
import cn.limbo.design_patterns.builder.PlayerBuilder;
import cn.limbo.design_patterns.builder.concrete_builder.FullModeBuilder;
import cn.limbo.design_patterns.builder.concrete_builder.KeepModeBuilder;
import cn.limbo.design_patterns.builder.concrete_builder.SimplifyModeBuilder;
import org.junit.Test;

/**
 * Created by limbo on 2016/12/10.
 */
public class BuilderTest {

	@Test
	public void show(){

		PlayerBuilder playerBuilder = new KeepModeBuilder();
		Player player = playerBuilder.build();
		System.out.println(player);

	}

}

3.優缺點

1.主要優點

(1) 在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。

(2) 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,使用者使用不同的具體建造者即可得到不同的產品物件。由於指揮者類針對抽象建造者程式設計,增加新的具體建造者無須修改原有類庫的程式碼,系統擴充套件方便,符合“開閉原則”

(3) 可以更加精細地控制產品的建立過程。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。

2.主要缺點

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

(2) 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加系統的理解難度和執行成本。


4.適用場景

在以下情況下可以考慮使用建造者模式:

(1) 需要生成的產品物件有複雜的內部結構,這些產品物件通常包含多個成員屬性。

(2) 需要生成的產品物件的屬性相互依賴,需要指定其生成順序。

(3) 物件的建立過程獨立於建立該物件的類。在建造者模式中通過引入了指揮者類,將建立過程封裝在指揮者類中,而不在建造者類和客戶類中。

(4) 隔離複雜物件的建立和使用,並使得相同的建立過程可以建立不同的產品。


5.與工廠模式的區別

建造者模式與抽象工廠模式有點相似,但是建造者模式返回一個完整的複雜產品,而抽象工廠模式返回一系列相關的產品;在抽象工廠模式中,客戶端通過選擇具體工廠來生成所需物件,而在建造者模式中,客戶端通過指定具體建造者型別並指導Director類如何去生成物件,側重於一步步構造一個複雜物件,然後將結果返回。如果將抽象工廠模式看成一個汽車配件生產廠,生成不同型別的汽車配件,那麼建造者模式就是一個汽車組裝廠,通過對配件進行組裝返回一輛完整的汽車。

6.與外觀模式的區別

外觀模式展現的是一堆事件的結果,而建造者模式只是返回了建造結束的實體類,例如泡茶,外觀模式反應出來的應該是取茶葉,燒開水,洗茶具,泡茶等過程性的東西,而建造者模式可能就是指返回茶葉,茶具等具體的東西了。也就是說外觀模式是一套行為的集合,而建造者模式是一堆零件的集合


相關文章