1. 程式人生 > >程式設計模式(二)建立型模式

程式設計模式(二)建立型模式

一、一個MAZE的構造過程

Maze的UML diagram如圖所示


可以發現MapSite是斜體表示抽象類,有Room, Door, Wall三個子類繼承,這三個就是用繼承封裝了MapSite的多向性。然而一個Maze,就是Room的聚合,所以Maze通過AddRoom方法來新增這些聚合這些Room. 最後通過MazeGame這個類,來構造這個Maze, 也就是幫助Maze這個物件執行它的AddRoom方法,幫Maze傳遞訊息。所以我說, MazeGame就是Maze的建立類。重點看這個MazeGame建立類

class MazeGame {
  public:
    Maze* CreateMaze();//本節將要介紹的建立方法
    
    Maze* CreateSimpleMaze();
    Maze* CreateMaze(MazeFactory&);//後面講的AbstractFactory模式
    Maze* CreateMaze(MazeBuilder&);//後面講的Builder模式
    
    Maze* CreateComplexMaze (MazeBuilder& builder);
    //構建各種不同迷宮的方法入口
};

看看CreateMaze()方法如何實現:

Maze* MazeGame::CreateMaze() {
    Maze* aMaze = new Maze;
    Room* r1 = new Room(1);
    Room* r2 = new Room(2);
    Door *theDoor = new Door(r1, r2);

    aMaze->AddRoom(r1);
    aMaze->AddRoom(r2);

    r1->SetSide(North, new Wall);
    r1->SetSide(East, theDoor);
    r1->SetSide(South, new Wall);
    r1->SetSide(West, new Wall);
    r2->SetSide(North, new Wall);
    r2->SetSide(East, new Wall);
    r2->SetSide(South, new Wall);
    r2->SetSide(West, theDoor);

    return aMaze;
}

可以看出,這個Maze被create出來的結構是單一的,就只可能是兩個房間,如何自由定義房間空間個數和四壁的內容而不重寫程式碼,這個問題後面是不能解決的。同時,the room, the wall, the door的性質,在SetSide裡面已經定義死了,如果下面我們需要給這個Maze定義兩種風格,第一種風格是room加上魔法room,door加上用魔法才能開啟的魔法Maze, 第二種風格room裡面可能有炸彈,走到炸彈裡面就死了的炸彈Maze, 用非繼承的方法,那麼我們呢需要重新寫兩個Room這個類,在EnchantedMaze中要擴充套件一個EnchantedRoom類,在BombedMaze中要擴充套件RoomWithABoom類,如下圖所示。


一般的思維,是在CreateMaze的時候,輸入的物件還是那個Maze, 但需要給Maze新增一個風格屬性,同時在CreateMaze()開頭就要新增一行語句判斷建立的是哪種風格的Maze, 然後分別對不同的Maze.style, 需要改Room* r1 = new Room(1) 為 Room* r1 = new EnchantedRoom(1) 和 Room* r1 = new RoomWithABomb(1) ,這樣一下子就增加了很多的程式碼量,因為case變多了。所以第一個想法就是調整CreateMaze的輸入,這樣引出了第一種模式AbstractFactory.

二、AbstractFactory

在我們定義一個基礎類AbstractFactory, 然後再派生出ConcreteFactory, 這每一個ConcreteFactory的類裡面都定義了各自創屬於自己風格的Room和Wall的方法。 帶入到Maze裡面的程式碼如下
class MazeFactory {
public:
    MazeFactory();
    virtual Maze* MakeMaze() const;
    virtual Wall* MakeWall() const;
    virtual Room* MakeRoom(int n) const;
    virtual Door* MakeDoor(Room* r1, Room* r2) const;
}; //這個是基礎的AbstractFactory,以後不同風格的ConcreteFactory都是繼承此。
//AbstractFactory提供建立Maze類物件的方法,並且提供建立Wall,Room,Door的方法,然後讓CreateMaze()來呼叫這個Factory中的方法建立Maze
Maze* MazeFactory::MakeMaze() const 
        { return new Maze; }
Wall* MazeFactory::MakeWall() const
        { return new Wall; }
Room* MazeFactory::MakeRoom(int n) const
        { return new Room(n); }
Door* MazeFactory::MakeDoor(Room* r1, Room* r2) const
        { return new Door(r1, r2); }
下面是實現了BomdedMazeFactory
class BombedMazeFactory : public MazeFactory {
public:
    BombedMazeFactory();
    virtual Wall* MakeWall() const;
    virtual Room* MakeRoom(int n) const;
};
Wall* BombedMazeFactory::MakeWall () const {
    return new BombedWall;
} 
Room* BombedMazeFactory::MakeRoom (int n) const {
    return new RoomWithABomb(n);
}注意這裡虛擬函式的重寫,沒有把Abstract中的所有虛擬函式都重寫,就重寫了需要改變的Room 和 Wall

同理是EnchantedMazeFactory的實現
class EnchantedMazeFactory : public MazeFactory {
public:
    EnchantedMazeFactory();
    virtual Room* MakeRoom(int n)  const;
    virtual Door* MakeDoor(Room* r1, Room* r2) const;
protected:
    Spell* CastSpell() const;
};
Room* EnchantedMazeFactory::MakeRoom(int n)  const
        { return new EnchantedRoom(n, CastSpell()); }
Door* EnchantedMazeFactory::MakeDoor(Room* r1, Room* r2)  const
        { return new DoorNeedingSpell(r1, r2); }
把上面這個放到CreateMaze()中就是如下所示,注意我們完全沒有必要粉case討論,直接輸入AbstractFactory基類型別就可以,
Maze* MazeGame::CreateMaze (MazeFactory& factory) {
    Maze* aMaze = factory.MakeMaze();
    Room* r1 = factory.MakeRoom(1);
    Room* r2 = factory.MakeRoom(2);
    Door* aDoor = factory.MakeDoor(r1, r2);

    aMaze->AddRoom(r1);
    aMaze->AddRoom(r2);

    r1->SetSide(North, factory.MakeWall());
    r1->SetSide(East, aDoor);
    r1->SetSide(South, factory.MakeWall());
    r1->SetSide(West, factory.MakeWall());
 
    r2->SetSide(North, factory.MakeWall());
    r2->SetSide(East, factory.MakeWall());
    r2->SetSide(South, factory.MakeWall());
    r2->SetSide(West, aDoor);
    return aMaze;
}
然後想要建立哪種風格的Maze,就輸入相應的ConcreteFactory, 輸入進去了就可以形成多型,CreateMaze()裡面的程式碼就一行不用改了。建立程式碼如下
BombedMazeFactory  BFactory;
EnchantedMazeFactory EFactory;

Maze* pBMaze = MazeGame::CreateMaze(BFactory);
Maze* pEMaze = MazeGame::CreateMaze(EFactory);
所以,現在就知道了如何利用AbstractFactory來通過AbstractFactory派生到ConcreteFactroy的過程,就在這些類裡面封裝了我們所需要的變化,因此在CreateMaze裡面就可以用單一的程式碼動態的展現我們的多種風格Maze的生成,這就是模式的精華。 這個裡面還要注意的是,MazeFactory這個類裡面不含純虛擬函式,可以直接生成物件,所以他不是抽象類。 但是注意,我們這裡一旦選定了factory的種類,CreateMaze的任務比如AddRoom(), SetSide()還是在CreateMaze()方法中顯示完成的,就與接下來的Builder模式不一樣了,因為Builder模式把上面Room的具體實現形式都封裝到了Builder物件中了。

2.Builder模式

迷宮Builder的基類
class MazeBuilder {
public:
    virtual void BuildMaze() { }
    virtual void BuildRoom(int room) { }
    virtual void BuildDoor(int roomFrom, int roomTo) { }

    virtual Maze* GetMaze() { return 0; }
protected:
    MazeBuilder();
};
MazeGame中CreateMaze()方法重新定義如下
Maze* MazeGame::CreateMaze (MazeBuilder& builder)
 {
    builder.BuildMaze();

    builder.BuildRoom(1);
    builder.BuildRoom(2);
    builder.BuildDoor(1, 2);

    return builder.GetMaze();
}
對比與AbstractFactory模式,可以發現CreateMaze中所有的對Maze製造過程都只依賴builder物件,不需要對Maze物件直接操作,而是把對Maze物件的操作放到了Builder裡面的方法中完成。當我們具體定義一個Builder的時候,通常要把基類的Builder繼承,如派生一個StandardMazeBuilder
class StandardMazeBuilder : public MazeBuilder {
public:
    StandardMazeBuilder();
    
    virtual void BuildMaze();
    virtual void BuildRoom(int);
    virtual void BuildDoor(int, int);
    
     virtual Maze* GetMaze();
private:
    Direction CommonWall(Room*, Room*);
    Maze* _currentMaze;
};
下面看建造Maze的方法,本來是要呼叫Maze物件來完成,但是現在是被封裝到了Builder物件中了
StandardMazeBuilder::StandardMazeBuilder () {
    _currentMaze = 0;
}
void StandardMazeBuilder::BuildMaze () {
    _currentMaze = new Maze;
}
Maze *StandardMazeBuilder::GetMaze () {
    Maze* maze =  _currentMaze;
    return maze;
}
void StandardMazeBuilder::BuildRoom (int n) {
    if (!_currentMaze->RoomNo(n)) {
        Room* room = new Room(n);
        _currentMaze->AddRoom(room);
        
        room->SetSide(North, new Wall);
        room->SetSide(South, new Wall);
        room->SetSide(East, new Wall);
        room->SetSide(West, new Wall);
    }
}
void StandardMazeBuilder::BuildDoor (int n1, int n2) {
    Room* r1 = _currentMaze->RoomNo(n1);
    Room* r2 = _currentMaze->RoomNo(n2);
    Door* d = new Door(r1, r2);
    
    r1->SetSide(CommonWall(r1,r2), d);
    r2->SetSide(CommonWall(r2,r1), d);
}
這樣一來,在CreateMaze()方法中程式碼就更簡單了,這樣完全將Maze的構建和Maze物件可以隔離開來,單純用builder物件就可以完成了。同理,對於不同風格的Maze的建立,需要繼承不同種類的Builder. 而且,builder的基類也不是抽象類,因為裡面如果有純虛擬函式一定要被實現,而實際中builder的派生出來的conceate builder中差別比較大,不一定每個都會用到基類中的純虛擬函式, 所以就沒必要採用抽象類來作為基類。 最終,採用下面程式碼來實現一個maze物件
void Test1() {
Maze* maze;
MazeGame game;
StandardMazeBuilder builder;

game.CreateMaze(builder);
maze = builder.GetMaze();
}
最後總結一下AbstractFactroy和Builder的聯絡和區別: 1. 兩個基類都不是抽象類 2. Builder比Abstract的封裝更隱蔽

相關推薦

程式設計模式建立模式

一、一個MAZE的構造過程 Maze的UML diagram如圖所示 可以發現MapSite是斜體表示抽象類,有Room, Door, Wall三個子類繼承,這三個就是用繼承封裝了MapSite的

java設計模式 建立模式 工廠模式 (簡單工廠,工廠方法,抽象工廠)

1.簡單工廠    定義:簡單工廠模式又 叫靜態工廠方法模式(Static FactoryMethod Pattern),是通過專門定義一個類來負責建立其他類的例項,被建立的例項通常都具有共同的父類。   2.工廠方法    定義:定

C++設計模式——建立模式

     設計模式指導我們怎樣去建立、維護、分配面向物件系統中的實體類, 以獲得高內聚、低耦合的面向物件系統,從而提高系統的可維護性和可複用性。設計模式是OO的一些設計思想的一個總結(但不是全部),因此設計模式和OO的設計原則經驗沒有矛盾,而是殊

java設計模式建立模式之 單例模式餓漢式,懶漢式,執行緒安全,雙重檢查

1.介紹     單例模式是一種常用的軟體設計模式,其定義是單例物件的類只能允許一個例項存在。  2.實現思路與步驟   1).將該類的構造方法定義為私有方法,這樣其他處的程式碼就無法通過呼叫該類的構造方法來例項化該類的物件,只有通過該類提供的靜態

簡談設計模式——建立模式

作為程式編寫技術的一個常見術語,以及技術筆試與面試的常考點,這裡總結一下設計模式的相關知識。 本文的程式碼實現均為Java。 什麼是設計模式 先看一些描述: 在軟體工程中,設計模式(design pattern)是對軟體設計中普遍存在(反覆

23種設計模式介紹---- 結構模式

implement weight 代碼 介紹 定義 裝飾器模式 大量 技術分享 記憶 由於設計模式篇幅比較大,如果在一篇文章講完所有的設計模式的話不利於閱讀。於是我把它分為三篇文章 23種設計模式介紹(一)---- 創建型模式 23種設計模式介紹(二)---- 結構

設計模式3—— 建立——建造者Builder

說明 在眾多開源框架或者jdk原始碼中常常出現Builder,build相關的類檔名或者類名,函式名。其中很多如此命名的原因就是因為使用了建造者(Builder)模式。檢視jdk原始碼不難發現,我們常用的StringBuilder類也使用了建造者模式。 建造者模式介

設計模式5—— 建立 —— 原型Prototype

導航 介紹原型模式的基本特點,物件拷貝的運用 。要理解 淺度拷貝 和 深度拷貝 的區別和使用。 原型設計模式介紹 定義:指原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件 特點:不需要知道任何建立細節,不呼叫建構函式 型別:建立型

設計模式4—— 建立 ——單例Singleton

導航 首先通過懶漢式的單例模式簡單程式碼實現作為開頭,發現有執行緒安全問題,並且在此懶漢模式程式碼上進行改進,衍生出同步懶漢設計模式,雙重檢查懶漢設計模式。另外還有靜態內部類方式實現單例,它是一種基於類初始化的延遲載入解決方案。 與懶漢式相對應的是餓漢式單例模式,其在類載入時就進

設計模式2——建立——工廠相關:簡單工廠Simple factory,工廠方法Factory method,抽象工廠Abstract factory

概要 這裡試圖描述23個設計模式中的兩個工廠(Factory)相關的設計模式:工廠方法(Factorymethod),抽象工廠(Abstract factory)。 注意點: 這兩個都屬於建立型設計模式。 由於這兩個設計模式都

java設計模式學習筆記--- 結構模式

文章目錄 介面卡模式 組合模式 裝飾模式 代理模式 什麼時候使用代理模式 享元模式 外觀模式(門面模式) 橋樑模式 介面卡模式 介面卡是一個介面轉換器,用於在接收不同的輸入時,得到一致

java設計模式學習筆記--- 建立模式

文章目錄 簡介 設計模式所遵循的幾個原則 一、工廠方法模式 簡單工廠模式 工廠方法模式 抽象工廠模式 工廠模式小結 單例模式 單例模式小結 建造者模式

設計模式簡單小例子結構模式

簡單小例子 原始碼已經上傳到了GitHub. https://github.com/tanglonghui/DesignPatterns 設計模式簡單小例子(一) 建立型模式: https://blog.csdn.net/qq_40687864/article/details/810

設計模式: 工廠模式

dem blank hibernate 執行 oid code 做出 void actor 工廠模式 工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。 在工廠模式中,我們在創建

設計模式---工廠方法模式

ack cto sys alt 修改 spa 抽象類 .com desc 1、簡介:工廠方法模式是類的創建模式,又叫虛擬構造子模式或是多態性工廠模式,它的實現方式是創建一個工廠接口,將實際創建對象的的工作轉移到工廠子類中,在系統的擴展中,可以在不修改工廠角色的情況下引進新的

Java設計模式7——結構模式之適配器模式

廠商 col err nds 類比 ref adapt extends 適配器模式 一、概述   概念      其實,舉個生活中的例子的話,適配器模式可以類比轉接頭,比如typeC和USB的轉接頭,把原本只能接typeC的接口,拓展為可以接普通USB;這裏的轉接頭一

常用的JavaScript設計模式Factory(工廠)模式

asc 我們 對象實例 actor 通過 汽車 對象 UNC cti Factory通過提供一個通用的接口來創建對象,同時,我們還可以指定我們想要創建的對象實例的類型。 假設現在有一個汽車工廠VehicleFactory,支持創建Car和Truck類型的對象實例,現在需要通

設計模式—— 觀察者模式

pda 發出 dex obs index observe target 獲取 委托事件 觀察者模式類似出版社與訂閱者的關系,訂閱者能夠收到出版社發出的消息,可以取消訂閱。出版社在觀察者模式中稱為主題(Subject)訂閱者稱為觀察者(Observer)。 主

設計模式:裝飾模式

實體 裝飾者 java string rgs 大氣 sta 接口 rri 2018國慶黃金周來了,恰值國慶黃金周節假日,我想高速上一定車山車海,還好我選擇了高鐵,不然肯定需要尋找項目合作人或在高速上吃創業人士的炒飯炒面了。 國慶7天長假,天氣又如此的好,所謂風和日麗,如此良