天天看點

設計模式-建造者模式(Builder)-Java1、示例案例-遊戲角色的設計2、建造者模式概述3、建造者模式與抽象工廠模式4、遊戲角色設計(建造者模式)完整解決方案5、關于Director的進一步讨論6、建造者模式總結

設計模式-建造者模式-Java

目錄

文章目錄

  • 1、示例案例-遊戲角色的設計
  • 2、建造者模式概述
    • 2.1、建造者模式定義
    • 2.2、建造者模式要點
    • 2.3、建造者模式結構圖中角色
    • 2.4、建造者模式典型代碼實作
  • 3、建造者模式與抽象工廠模式
  • 4、遊戲角色設計(建造者模式)完整解決方案
  • 5、關于Director的進一步讨論
    • 5.1、省略Director
    • 5.2、鈎子方法的引入
  • 6、建造者模式總結
    • 6.1、優缺點
    • 6.2、适用場景
    • ***後記*** :
内容

  沒有人買車會隻買一個輪胎或者方向盤,大家買的都是一輛包含輪胎、方向盤和發動機等多個部件的完整汽車。如何将這些部件組裝成一輛完整的汽車并傳回給使用者,這是建造者模式

需要解決的問題。建造者模式又稱為生成器模式,它是一種較為複雜、使用頻率也相對較低的建立型模式。建造者模式為用戶端傳回的不是一個簡單的産品,而是一個由多個部件組成的複雜産品。

1、示例案例-遊戲角色的設計

  Sunny軟體公司遊戲開發小組決定開發一款名為《Sunny群俠傳》的網絡遊戲,該遊戲采用主流的RPG(Role Playing Game,角色扮演遊戲)模式,玩家可以在遊戲中扮演虛拟世界中的一個特定角色,角色根據不同的遊戲情節和統計資料(如力量、魔法、技能等)具有不同的能力,角色也會随着不斷更新而擁有更加強大的能力。

  作為RPG遊戲的一個重要組成部分,需要對遊戲角色進行設計,而且随着該遊戲的更新将不斷增加新的角色。不同類型的遊戲角色,其性别、臉型、服裝、發型等外部特性都有所差

異,例如“天使”擁有美麗的面容和披肩的長發,并身穿一襲白裙;而“惡魔”極其醜陋,留着光頭并穿一件刺眼的黑衣。 Sunny公司決定開發一個小工具來建立遊戲角色,可以建立不同類型的角色并可以靈活增加新的角色。

  Sunny公司的開發人員通過分析發現,遊戲角色是一個複雜對象,它包含性别、臉型等多個組成部分,不同的遊戲角色其組成部分有所差異,如圖1-1所示:

設計模式-建造者模式(Builder)-Java1、示例案例-遊戲角色的設計2、建造者模式概述3、建造者模式與抽象工廠模式4、遊戲角色設計(建造者模式)完整解決方案5、關于Director的進一步讨論6、建造者模式總結
設計模式-建造者模式(Builder)-Java1、示例案例-遊戲角色的設計2、建造者模式概述3、建造者模式與抽象工廠模式4、遊戲角色設計(建造者模式)完整解決方案5、關于Director的進一步讨論6、建造者模式總結
設計模式-建造者模式(Builder)-Java1、示例案例-遊戲角色的設計2、建造者模式概述3、建造者模式與抽象工廠模式4、遊戲角色設計(建造者模式)完整解決方案5、關于Director的進一步讨論6、建造者模式總結

圖1-1 幾種不同的遊戲角色(注:本圖中的遊戲角色造型來源于網絡,特此說明)

  無論是何種造型的遊戲角色,它的建立步驟都大同小異,都需要逐漸建立其組成部分,再将各組成部分裝配成一個完整的遊戲角色。如何一步步建立一個包含多個組成部分的複雜對

象,建造者模式為解決此類問題而誕生。

2、建造者模式概述

  建造者模式是較為複雜的建立型模式,它将用戶端與包含多個組成部分(或部件)的複雜對象的建立過程分離,用戶端無須知道複雜對象的内部組成部分與裝配方式,隻需要知道所需

建造者的類型即可。它關注如何一步一步建立一個的複雜對象,不同的具體建造者定義了不同的建立過程,且具體建造者互相獨立,增加新的建造者非常友善,無須修改已有代碼,系統具有較好的擴充性。

2.1、建造者模式定義

  建造者模式(Builder Pattern):将一個複雜對象的建構與塔的表示分離,使得同樣的建構過程可以建立不同的表示。建構者模式是一種對象建立型模式。

2.2、建造者模式要點

  建造者模式一步一步建立一個複雜的對象,它允許使用者隻通過指定複雜對象的類型和内容就可以建構它們,使用者不需要知道内部的具體建構細節。建造者模式結構如圖2.2-1所示:

設計模式-建造者模式(Builder)-Java1、示例案例-遊戲角色的設計2、建造者模式概述3、建造者模式與抽象工廠模式4、遊戲角色設計(建造者模式)完整解決方案5、關于Director的進一步讨論6、建造者模式總結

圖2.2-1 建造者模式結構圖

2.3、建造者模式結構圖中角色

  建造者模式結構圖中包含如下幾個角色:

  • Builder(抽象建造者):它為建立一個産品Product對象的各個部件指定抽象接口,在該接口中一般聲明兩類方法,一類方法是buildPartX(),它們用于建立複雜對象的各個部件;另一類方法是getResult(),它們用于傳回複雜對象。Builder既可以是抽象類,也可以是接口。
  • ConcreteBuilder(具體建造者):它實作了Builder接口,實作各個部件的具體構造和裝配方法,定義并明确它所建立的複雜對象,也可以提供一個方法傳回建立好的複雜産品對象。
  • Product(産品角色):它是被建構的複雜對象,包含多個組成部件,具體建造者建立該産品的内部表示并定義它的裝配過程。
  • Director(指揮者):指揮者又稱為導演類,它負責安排複雜對象的建造次序,指揮者與抽象建造者之間存在關聯關系,可以在其construct()建造方法中調用建造者對象的部件構造與裝配方法,完成複雜對象的建造。用戶端一般隻需要與指揮者進行互動,在用戶端确定具體建造者的類型,并執行個體化具體建造者對象(也可以通過配置檔案和反射機制),然後通過指揮者類的構造函數或者Setter方法将該對象傳入指揮者類中。

2.4、建造者模式典型代碼實作

  • 産品代碼2.4-1:
    class Product {
      	private String partA; //定義部件,部件可以是任意類型,包括值類型和引用類型
      	private String partB;
      	private String partC;
      	//partA的Getter方法和Setter方法省略
      	//partB的Getter方法和Setter方法省略
      	//partC的Getter方法和Setter方法省略
      }
               
  • 抽象建造者帶2.4-2:
    abstract class Builder {
      	//建立産品對象
      	protected Product product=new Product();
      	public abstract void buildPartA();
      	public abstract void buildPartB();
      	public abstract void buildPartC();
      	//傳回産品對象
      	public Product getResult() {
      		return product;
      	}
      }
               

  在抽象類Builder中聲明了一系列抽象的buildPartX()方法用于建立複雜産品的各個部件,具體建造過程在ConcreteBuilder中實作,此外還提供了工廠方法getResult(),用于傳回一個建造好的完整産品。

  在ConcreteBuilder中實作了buildPartX()方法,通過調用Product的setPartX()方法可以給産品對象的成員屬性設值。不同的具體建造者在實作buildPartX()方法時将有所差別,如setPartX()方法的參數可能不一樣,在有些具體建造者類中某些setPartX()方法無須實作(提供一個空實作)。而這些對于用戶端來說都無須關心,用戶端隻需知道具體建造者類型即可。

  在建造者模式的結構中還引入了一個指揮者類Director,該類主要有兩個作用:一方面它隔離了客戶與建立過程;另一方面它控制産品的建立過程,包括某個buildPartX()方法是否被調用以及多個buildPartX()方法調用的先後次序等。指揮者針對抽象建造者程式設計,用戶端隻需要知道具體建造者的類型,即可通過指揮者類調用建造者的相關方法,傳回一個完整的産品對象。在實際生活中也存在類似指揮者一樣的角色,如一個客戶去購買電腦,電腦銷售人員相當于指揮者,隻要客戶确定電腦的類型,電腦銷售人員可以通知電腦組裝人員給客戶組裝一台電腦。

  • 指揮者代碼2.4-4:
    class Director {
      	private Builder builder;
      	public Director(Builder builder) {
      		this.builder=builder;
      	}
      	public void setBuilder(Builder builder) {
      		this.builder=builer;
      	}
      	//産品建構與組裝方法
      	public Product construct() {
      		builder.buildPartA();
      		builder.buildPartB();
      		builder.buildPartC();
      		return builder.getResult();
      	}
      }
               

  在指揮者類中可以注入一個抽象建造者類型的對象,其核心在于提供了一個建造方法

construct(),在該方法中調用了builder對象的構造部件的方法,最後傳回一個産品對象。對于用戶端而言,隻需關心具體的建造者即可。

  • 用戶端類代碼2.4-5片段如下所示:
    ……
      Builder builder = new ConcreteBuilder(); //可通過配置檔案實作
      Director director = new Director(builder);
      Product product = director.construct();
      ……
               

  可以通過配置檔案來存儲具體建造者類ConcreteBuilder的類名,使得更換新的建造者時無須修改源代碼,系統擴充更為友善。在用戶端代碼中,無須關心産品對象的具體組裝過程,隻需指定具體建造者的類型即可。

3、建造者模式與抽象工廠模式

  建造者模式與抽象工廠模式有點相似,但是建造者模式傳回一個完整的複雜産品,而抽象工廠模式傳回一系列相關的産品;在抽象工廠模式中,用戶端通過選擇具體工廠來生成所需對

象,而在建造者模式中,用戶端通過指定具體建造者類型并指導Director類如何去生成對象,側重于一步步構造一個複雜對象,然後将結果傳回。如果将抽象工廠模式看成一個汽車配件生産廠,生成不同類型的汽車配件,那麼建造者模式就是一個汽車組裝廠,通過對配件進行組裝傳回一輛完整的汽車。

4、遊戲角色設計(建造者模式)完整解決方案

  Sunny公司開發人員決定使用建造者模式來實作遊戲角色的建立,其基本結構如圖4-1所示:

設計模式-建造者模式(Builder)-Java1、示例案例-遊戲角色的設計2、建造者模式概述3、建造者模式與抽象工廠模式4、遊戲角色設計(建造者模式)完整解決方案5、關于Director的進一步讨論6、建造者模式總結

圖4-1 遊戲角色建立圖

  在圖4-1中,ActorController充當指揮者,ActorBuilder充當抽象建造者,HeroBuilder、AngelBuilder和DevilBuilder充當具體建造者,Actor充當複雜産品。完整代碼如下所示:

  • 角色類代碼4-1:
    //Actor角色類:複雜産品,考慮到代碼的可讀性,隻列出部分成員屬性,且成員屬性的類型均
      為String,真實情況下,有些成員屬性的類型需自定義
      
      package builder;
    
      // Actor角色類:複雜産品
      public class Actor {
      	private String type; // 角色類型
      	private String sex; // 性别
      	private String face; // 臉型
      	private String costume; // 服飾
      	private String hairstyle; // 發型
    
      	public Actor() {}
    
      	public String getType() {
      		return type;
      	}
    
      	public void setType(String type) {
      		this.type = type;
      	}
    
      	public String getSex() {
      		return sex;
      	}
    
      	public void setSex(String sex) {
      		this.sex = sex;
      	}
    
      	public String getFace() {
      		return face;
      	}
    
      	public void setFace(String face) {
      		this.face = face;
      	}
    
      	public String getCostume() {
      		return costume;
      	}
    
      	public void setCostume(String costume) {
      		this.costume = costume;
      	}
    
      	public String getHairstyle() {
      		return hairstyle;
      	}
    
      	public void setHairstyle(String hairstyle) {
      		this.hairstyle = hairstyle;
      	}
      }
               
  • 角色建造器類代碼4-2:
    package builder;
    
      // 角色建造器:抽象建造者
      public abstract class ActorBuilder {
      	protected Actor actor = new Actor();
    
      	public abstract void buildType();
      	public abstract void buildSex();
      	public abstract void buildFace();
      	public abstract void buildCostume();
      	public abstract void buildHairstyle();
    
      	// 工廠方法,傳回一個完整的遊戲角色對象
      	public Actor createActor() {
      		return this.actor;
      	}
      }
               
  • 英雄角色建造器類代碼4-3:
    package builder;
    
      //英雄角色建造器:具體建造者
      public class HeroBuilder extends ActorBuilder{
    
      	@Override
      	public void buildType() {
      		actor.setType("英雄");
      	}
    
      	@Override
      	public void buildSex() {
      		actor.setSex("男");
      	}
    
      	@Override
      	public void buildFace() {
      		actor.setFace("英俊");
      	}
    
      	@Override
      	public void buildCostume() {
      		actor.setCostume("盔甲");
      	}
    
      	@Override
      	public void buildHairstyle() {
      		actor.setHairstyle("飄逸");
      	}
      }
               
  • 天使角色建造器類代碼4-4:
    package builder;
    
      //天使角色建造器:具體建造者
      public class AngelBuilder extends ActorBuilder{
    
      	@Override
      	public void buildType() {
      		actor.setType("天使");
      	}
    
      	@Override
      	public void buildSex() {
      		actor.setSex("女");
      	}
    
      	@Override
      	public void buildFace() {
      		actor.setFace("漂亮");
      	}
    
      	@Override
      	public void buildCostume() {
      		actor.setCostume("白裙");
      	}
    
      	@Override
      	public void buildHairstyle() {
      		actor.setHairstyle("披肩長發");
      	}
      }
               
  • 惡魔角色建造器類代碼4-5:
    package builder;
    
      //惡魔角色建造器:具體建造者
      public class DevilBuilder extends ActorBuilder {
    
      	@Override
      	public void buildType() {
      		actor.setType("惡魔");
      	}
    
      	@Override
      	public void buildSex() {
      		actor.setSex("妖");
      	}
    
      	@Override
      	public void buildFace() {
      		actor.setFace("醜陋");
      	}
    
      	@Override
      	public void buildCostume() {
      		actor.setCostume("黑衣");
      	}
    
      	@Override
      	public void buildHairstyle() {
      		actor.setHairstyle("光頭");
      	}
      }
               
  • 指揮者類代碼4-6:
    package builder;
    
      //遊戲角色建立控制器:指揮者
      public class ActorController {
    
      	//逐漸建構複雜産品對象
      	public Actor construct(ActorBuilder ab) {
      		ab.buildType();
      		ab.buildSex();
      		ab.buildFace();
      		ab.buildCostume();
      		ab.buildHairstyle();
      		return ab.createActor();
      	}
      }
               
  • 工具類代碼4-7:
    package builder;
    
      import java.util.Properties;
    
      // 工具類
      public class Utils {
    
      	// 傳回角色建造者對象
      	public static ActorBuilder getActorBuilder() {
      		try {
      			Properties prop = new Properties();
      			prop.load(Utils.class.getClassLoader().getResourceAsStream("actor.properties"));
      			String className = prop.getProperty("className");
      			return (ActorBuilder)(Class.forName(className).newInstance());
      		}catch (Exception e) {
      			e.printStackTrace();
      			return null;
      		}
      	}
      }
               
  • 測試類代碼4-8:
    package builder;
    
      public class TestActor {
      	public static void main(String args[])
      	{
      		ActorBuilder ab; //針對抽象建造者程式設計
      		ab = Utils.getActorBuilder(); //反射生成具體建造者對象
      		ActorController ac = new ActorController();
      		Actor actor;
      		actor = ac.construct(ab); //通過指揮者建立完整的建造者對象
      		String type = actor.getType();
      		System.out.println(type + "的外觀:");
      		System.out.println("性别:" + actor.getSex());
      		System.out.println("面容:" + actor.getFace());
      		System.out.println("服裝:" + actor.getCostume());
      		System.out.println("發型:" + actor.getHairstyle());
      		}
      }
               
  • 測試結果:
    天使的外觀:
      性别:女
      面容:漂亮
      服裝:白裙
      發型:披肩長發
               

  在建造者模式中,用戶端隻需執行個體化指揮者類,指揮者類針對抽象建造者程式設計,用戶端根據需要傳入具體的建造者類型,指揮者将指導具體建造者一步一步構造一個完整的産品(逐漸

調用具體建造者的buildX()方法),相同的構造過程可以建立完全不同的産品。在遊戲角色執行個體中,如果需要更換角色,隻需要修改配置檔案,更換具體角色建造者類即可;如果需要增加新角色,可以增加一個新的具體角色建造者類作為抽象角色建造者的子類,再修改配置檔案即可,原有代碼無須修改,完全符合“開閉原則”。

5、關于Director的進一步讨論

5.1、省略Director

  在有些情況下,為了簡化系統結構,可以将Director和抽象建造者Builder進行合并,在Builder中提供逐漸建構複雜産品對象的construct()方法。由于Builder類通常為抽象類,是以可以将construct()方法定義為靜态(static)方法。如果将遊戲角色設計中的指揮者類ActorController省

略,ActorBuilder類的代碼5.1-1修改如下:

abstract class ActorBuilder
{
	protected static Actor actor = new Actor();
	public abstract void buildType();
	public abstract void buildSex();
	public abstract void buildFace();
	public abstract void buildCostume();
	public abstract void buildHairstyle();
	public static Actor construct(ActorBuilder ab) {
		ab.buildType();
		ab.buildSex();
		ab.buildFace();
		ab.buildCostume();
		ab.buildHairstyle();
		return actor;
	}
}
           

對應的用戶端代碼也将發生修改,其代碼片段5.1-2如下所示:

……
ActorBuilder ab;
ab = Utils.getActorBuilder();
Actor actor;
actor = ActorBuilder.construct(ab);
……
           

除此之外,還有一種更簡單的處理方法,可以将construct()方法的參數去掉,直接在construct()

方法中調用buildPartX()方法,代碼如5.1-3下所示:

abstract class ActorBuilder {
	protected Actor actor = new Actor();
	public abstract void buildType();
	public abstract void buildSex();
	public abstract void buildFace();
	public abstract void buildCostume();
	public abstract void buildHairstyle();
	public Actor construct() {
		this.buildType();
		this.buildSex();
		this.buildFace();
		this.buildCostume();
		this.buildHairstyle();
		return actor;
	}
}
           

用戶端代碼代碼片段5.1-4如下所示:

……
ActorBuilder ab;
ab = Utils.getActorBuilder();
Actor actor;
actor = ab.construct();
……
           

  此時,construct()方法定義了其他buildPartX()方法調用的次序,為其他方法的執行提供了一個流程模闆,這與我們在後面要學習的模闆方法模式非常類似。

  以上兩種對Director類的省略方式都不影響系統的靈活性和可擴充性,同時還簡化了系統結構,但加重了抽象建造者類的職責,如果construct()方法較為複雜,待建構産品的組成部分較

多,建議還是将construct()方法單獨封裝在Director中,這樣做更符合“單一職責原則”。

5.2、鈎子方法的引入

  建造者模式除了逐漸建構一個複雜産品對象外,還可以通過Director類來更加精細地控制産品的建立過程,例如增加一類稱之為鈎子方法(HookMethod)的特殊方法來控制是否對某個

buildPartX()的調用。

  鈎子方法的傳回類型通常為boolean類型,方法名一般為isXXX(),鈎子方法定義在抽象建造者類中。例如我們可以在遊戲角色的抽象建造者類ActorBuilder中定義一個方法isBareheaded(),用于判斷某個角色是否為“光頭(Bareheaded)”,在ActorBuilder為之提供一個預設實作,其傳回值為false,代碼5.2-1如下所示:

abstract class ActorBuilder {
	protected Actor actor = new Actor();
	public abstract void buildType();
	複雜對象的組裝與建立——建造者模式(三)
	127
	public abstract void buildSex();
	public abstract void buildFace();
	public abstract void buildCostume();
	public abstract void buildHairstyle();
	//鈎子方法
	public boolean isBareheaded() {
		return false;
	}
	public Actor createActor() {
		return actor;
	}
}
           

如果某個角色無須建構頭發部件,例如“惡魔(Devil)”,則對應的具體建造器DevilBuilder将覆

蓋isBareheaded()方法,并将傳回值改為true,代碼5.2-2如下所示:

class DevilBuilder extends ActorBuilder {
	public void buildType() {
		actor.setType("惡魔");
	}
	public void buildSex() {
		actor.setSex("妖");
	}
	public void buildFace() {
		actor.setFace("醜陋");
	}
	public void buildCostume() {
		actor.setCostume("黑衣");
	}
	public void buildHairstyle() {
		actor.setHairstyle("光頭");
	}
	//覆寫鈎子方法
	public boolean isBareheaded() {
		return true;
	}
}
           

此時,指揮者類ActorController的代碼5.2-3修改如下:

class ActorController {
	public Actor construct(ActorBuilder ab) {
		Actor actor;
		ab.buildType();
		ab.buildSex();
		ab.buildFace();
		ab.buildCostume();
		//通過鈎子方法來控制産品的建構
		if(!ab.isBareheaded()) {
			ab. buildHairstyle();
		}
		actor=ab.createActor();
		return actor;
	}
}
           

  當在用戶端代碼中指定具體建造者類型并通過指揮者來實作産品的逐漸建構時,将調用鈎子方法isBareheaded()來判斷遊戲角色是否有頭發,如果isBareheaded()方法傳回true,即沒有頭發,則跳過建構發型的方法buildHairstyle();否則将執行buildHairstyle()方法。通過引入鈎子方

法,我們可以在Director中對複雜産品的建構進行精細的控制,不僅指定buildPartX()方法的執行順序,還可以控制是否需要執行某個buildPartX()方法。

6、建造者模式總結

  建造者模式的核心在于如何一步一步建構一個包含多個組成部件的完整對象,使用相同的建構過程建構不同的産品,在軟體開發中,如果我們需要建立複雜對象并希望系統具備很好的靈活性和可擴充性可以考慮使用建造者模式。

6.1、優缺點

  • 主要優點:
    • (1):在建造者模式中,用戶端不必知道産品内部組成的細節,将産品本身與産品的建立過程解耦,使得相同的建立過程可以建立不同的産品對象。
    • (2):每一個具體的建造者都相對獨立,而與其他具體建造者無關,是以可以很方法的替換具體建造者或者增删新的具體建造者,使用者使用不同的具體建造者即可得到不同的産品對象。由于指揮者類針對抽象建造者程式設計,增加薪的具體建造者無須修改原有類庫的代碼,系統擴充友善,符合“開閉原則”。
    • (3):可以更加精确地控制産品的建立過程。将複雜産品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也友善使用程式來控制建立過程。
  • 主要缺點:
    • (1):建造者模式所建立的産品一般具有較多的共同點,其組成部分相似,如果産品之間的差異性很大,例如很多組成部分都不相同,不适合使用建造者模式,是以使用範圍受到一定的限制。
    • (2):如果産品的内部變化複雜,可能導緻定義很多的具體建造者類來實作這種變化,導緻系統變得很龐大,增加系統的了解難度和運作成本。

6.2、适用場景

  • (1) 需要生成的産品對象有複雜的内部結構,這些産品對象通常包含多個成員屬性。
  • (2) 需要生成的産品對象的屬性互相依賴,需要指定其生成順序。
  • (3) 對象的建立過程獨立于建立該對象的類。在建造者模式中通過引入了指揮者類,将建立過程封裝在指揮者類中,而不在建造者類和客戶類中。
  • (4) 隔離複雜對象的建立和使用,并使得相同的建立過程可以建立不同的産品

後記 :

  本項目為參考某馬視訊開發,相關視訊及配套資料可自行度娘或者聯系本人。上面為自己編寫的開發文檔,持續更新。歡迎交流,本人QQ:806797785

前端項目源代碼位址:https://gitee.com/gaogzhen/vue-leyou
後端JAVA源代碼位址:https://gitee.com/gaogzhen/JAVA