天天看點

建立型設計模式--建造者模式

記錄結構:

--1.前言
          --2.實際問題引入(需求)
          --3.使用建造者模式解決問題
             --3.1建造者模式簡述
             --3.2建造者模式類圖
             --3.3建造者模式完整解決方案
          --4.建造者模式總結
           

1.前言

沒有人買車會隻買一個輪胎或者方向盤,大家買的都是一輛包含輪胎、方向盤和發動機等多個部件的完整汽車。如何将這些部件組裝成一輛完整的汽車并傳回給使用者,這是建造者模式需要解決的問題。建造者模式又稱為生成器模式,它是一種較為複雜、使用頻率也相對較低的建立型模式。建造者模式為用戶端傳回的不是一個簡單的産品,而是一個由多個部件組成的複雜産品。

2.實際問題引入(需求)

遊戲角色設計

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

作為 RPG 遊戲的一個重要組成部分,需要對遊戲角色進行設計,而且随着該遊戲的更新将不斷增加新的角色。不同類型的遊戲角色,其性别、臉型、服裝、發型等外部特性都有所差異,例如“天使”擁有美麗的面容和披肩的長發,并身穿一襲白裙;而“惡魔”極其醜陋,留着光頭并穿一件刺眼的黑衣。

YY公司決定開發一個小工具來建立遊戲角色,可以建立不同類型的角色并可以靈活增加新的角色。

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

建立型設計模式--建造者模式

建立型設計模式.gif (注:本圖中的遊戲角色造型來源于網絡,特此說明)

3.使用建造者模式解決問題

3.1.建造者模式簡述:

建造者模式是較為複雜的建立型模式,它将用戶端與包含多個組成部分(或部件)的複雜對象的建立過程分離,用戶端無須知道複雜對象的内部組成部分與裝配方式,隻需要知道所需建造者的類型即可。它關注如何一步一步建立一個的複雜對象,不同的具體建造者定義了不同的建立過程,且具體建造者互相獨立,增加新的建造者非常友善,無須修改已有代碼,系統具有較好的擴充性。

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

3.2.建造者模式類圖:

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

建立型設計模式--建造者模式

建造者模式類圖.gif

各元件詳解:

Builder(抽象建造者):它為建立一個産品 Product 對象的各個部件指定抽象接口,在該接口中一般聲明兩類方法,一類方法是 buildPartX(),它們用于建立複雜對象的各個部件;另一類方法是 getResult(),它們用于傳回複雜對象。Builder 既可以是抽象類,也可以是接口。

ConcreteBuilder(具體建造者):它實作了 Builder 接口,實作各個部件的具體構造和裝配方法,定義并明确它所建立的複雜對象,也可以提供一個方法傳回建立好的複雜産品對象。

Product(産品角色):它是被建構的複雜對象,包含多個組成部件,具體建造者建立該産品的内部表示并定義它的裝配過程。

Director(指揮者):指揮者又稱為導演類,它負責安排複雜對象的建造次序,指揮者與抽象建造者之間存在聚合關系,可以在其 construct() 建造方法中調用建造者對象(使用多态,其實調用的是具體建造者的對象)的部件構造與裝配方法,完成複雜對象的建造。用戶端一般隻需要與指揮者進行互動,在用戶端确定具體建造者的類型,并執行個體化具體建造者對象(也可以通過配置檔案和反射機制),然後通過指揮者類的構造函數或者 Setter 方法将該對象(具體建造者)傳入指揮者類中。

3.3建造者模式完整解決方案

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

建立型設計模式--建造者模式

建造者模式解決遊戲人物建立結構類圖.gif

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

//Actor角色類:複雜産品,考慮到代碼的可讀性,隻列出部分成員屬性,
//且成員屬性的類型均為String,真實情況下,有些成員屬性的類型需自定義
class Actor
{
       private  String type; //角色類型
       private  String sex; //性别
       private  String face; //臉型
       private  String costume; //服裝
       private  String hairstyle; //發型

       public  void setType(String type) {
              this.type  = type;
       }
       public  void setSex(String sex) {
              this.sex  = sex;
       }
       public  void setFace(String face) {
              this.face  = face;
       }
       public  void setCostume(String costume) {
              this.costume  = costume;
       }
       public  void setHairstyle(String hairstyle) {
              this.hairstyle  = hairstyle;
       }
       public  String getType() {
              return  (this.type);
       }
       public  String getSex() {
              return  (this.sex);
       }
       public  String getFace() {
              return  (this.face);
       }
       public  String getCostume() {
              return  (this.costume);
       }
       public  String getHairstyle() {
              return  (this.hairstyle);
       }
}

//角色建造器:抽象建造者
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 actor;
       }
}

//英雄角色建造器:具體建造者
class HeroBuilder 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("飄逸");
       }    
}

//天使角色建造器:具體建造者
class AngelBuilder 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("披肩長發");
       }    
}

//惡魔角色建造器:具體建造者
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("光頭");
       }    
}
           

指揮者類 ActorController 定義了 construct() 方法,該方法擁有一個抽象建造者 ActorBuilder 類型的參數,在該方法内部實作了遊戲角色對象的逐漸建構,代碼如下所示:

//遊戲角色建立控制器:指揮者
class ActorController
{
    //逐漸建構複雜産品對象
       public Actor construct(ActorBuilder ab)
       {
              Actor actor;
              ab.buildType();
              ab.buildSex();
              ab.buildFace();
              ab.buildCostume();
              ab.buildHairstyle();
              actor=ab.createActor();
              return actor;
       }
}
           

為了提高系統的靈活性和可擴充性,我們将具體建造者類的類名存儲在配置檔案中,并通過工具類 XMLUtil 來讀取配置檔案并反射生成對象,XMLUtil 類的代碼如下所示:

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
class XMLUtil
{
//該方法用于從XML配置檔案中提取具體類類名,并傳回一個執行個體對象
       public  static Object getBean()
       {
              try
              {
                     //建立文檔對象
                     DocumentBuilderFactory  dFactory = DocumentBuilderFactory.newInstance();
                     DocumentBuilder  builder = dFactory.newDocumentBuilder();
                     Document  doc;                                                
                     doc  = builder.parse(new File("config.xml"));

                     //擷取包含類名的文本節點
                     NodeList  nl = doc.getElementsByTagName("className");
            Node  classNode=nl.item(0).getFirstChild();
            String  cName=classNode.getNodeValue();

            //通過類名生成執行個體對象并将其傳回
            Class c=Class.forName(cName);
                 Object obj=c.newInstance();
            return obj;
         }  
         catch(Exception e)
         {
              e.printStackTrace();
              return null;
          }
       }
}
           

編寫如下用戶端測試代碼:

class Client {
       public  static void main(String args[])
       {
              ActorBuilder ab; //針對抽象建造者程式設計
              ab =  (ActorBuilder)XMLUtil.getBean(); //反射生成具體建造者對象

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

4.建造者模式總結

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

主要優點

建造者模式的主要優點如下:

(1) 在建造者模式中,用戶端不必知道産品内部組成的細節,将産品本身與産品的建立過程解耦,使得相同的建立過程可以建立不同的産品對象。

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

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

主要缺點

建造者模式的主要缺點如下:

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

(2) 如果産品的内部變化複雜,可能會導緻需要定義很多具體建造者類來實作這種變化,導緻系統變得很龐大,增加系統的了解難度和運作成本。

适用場景

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

(1) 需要生成的産品對象有複雜的内部結構,這些産品對象通常包含多個成員屬性。

(2) 需要生成的産品對象的屬性互相依賴,需要指定其生成順序。

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

(4) 隔離複雜對象的建立和使用,并使得相同的建立過程可以建立不同的産品。

特此說明:文章引用于

極客學院--建立型設計模式
部落格搬家: 大坤的個人部落格 歡迎評論哦~

繼續閱讀