Design Pattern 小筆記

所有的練習我有開一個Github Project




模式名稱

模式描述、說明
模式程式碼





簡單工廠模式

實作注意:建立一個static function(工廠function)決定(或選擇)建造物件是什麼。
利用工廠模式決定產出的物件是什麼(這些物件將會執行相同的行為)
把creatSomthing放在factory類別中,並且設定成static,使用方式像下面這樣
something = factory::creatSomething(someThingType);  //靜態工廠
工廠做出來的物件,會有相同的一組函式介面,意思是new出來的物件會執行相同的function然後產出物件,並且後續以相同的介面使用該物件。

策略模式

實作注意:不同的function(演算法)使用同一組interface,繼承其interface的class目的在於呼叫其內含的function。
利用策略決定(選擇)使用的function(或演算法)是什麼。
  1. 將interface放在class中,再繼承interface實作各種演算法。
  2. 將各種演算法放在同一個class,重新定義class的operator()。
omeTea BlackTea(TT_BLACKTEA);
BlackTea.shakeStrategy();  //介面=概念執行,不代表演算法實作
將演算法打包,我利用operator()()來實作。將演算法包進一個類別裡。(偽造的function)

裝飾模式

call back function實現在物件導向中的做法。
裝飾類別是被call back的function,本體類別是原本的觸發點,使用時才決定call back哪些function,使用相同的interface依序呼叫function。
本體與裝飾虛類別繼承自相同的interface。
Ice->SetDecorator(blackTea);  //設定觸發本體
Milk->SetDecorator(Ice);   //設定觸發本體時,同時又要觸發什麼(裝飾)...
//...
Milk.CreatDrank();
裝飾的設定可以用重載運算子串起來(+或-)。
blackTea + Ice + Milk /* +... */  //設定觸發的本體+觸發裝飾+觸發裝飾;
Milk.CreatDrank();
裝飾品裝在本體上,也可以裝在裝飾品上,最後用一個相同的function來串起來(call back)。
通常用在程式執行時才可以決定到底這次要使用哪些function,所以先全部做出來,到時再用裝飾模式

代理模式

實作注意:設計一個class,擁有原本無法修改設計的class,並且與它繼承自同一個基礎類別。
不修改原本的class之下,透明的擴充,擴充特性本身就是proxy的特色
class proxy
{
    Excelfile* xlsf;  //使用代理模式擴充excel的類別
};
代理與被代理是衍生自同一個父類別。
看似沒有什麼特色的代理模式,可以用來增加介面反應速度(buffer)。
使用一組function取代(擴充)真正接觸實體物件時付出的代價。
  • 遠端代理(Remote):代理遠端程時執行,例如我們可以透過WebService的WSDL定義產生中介檔的函式庫,透過這個函式庫就可以存取WebService。
  • 虛擬代理(Virtual):將需要秏費大量時間或是複雜的實體,利用代理模式的物件代替。
  • 安全代理(Protect or Access):控制物件存取時的許可權。
  • 智慧參考(Smart Reference):提供比原有物件更多的服務。

工廠方法模式

建構過程做在工廠裡的function,有時建構參數很多,有時建構時要透過另一個臨時的物件當作參數,要先建構它,再建構主要的物件。
建立自訂建構過程的construction function群當工廠

將變動留給使用者
簡單工廠簡化了使用者的程式碼,卻需要不斷的變動工廠的程式碼才可以新增項目
工廠方法簡化了新增項目的部份,雖然看似複雜了使用者的程式碼。
//用這個決定物件是誰(不過,若要使用switch-case就...和簡單工廠一樣了XD
IFactory* factory = new ModFactory();  //在此決定要建的工廠function是什麼(工廠選擇,決定產線內容不同)
Operation* oper = factory->creatOperation();  //在這call construct function
工廠從簡單工廠→工廠方法之外,書上還有提到一種「反射」,之後怎麼做就繼續把書看下去吧!

原型模式

複製指標指向的物件。
利用函數複製「指標指向的物件」(包含物件內的成員屬性本身)
淺複製: 遇到成員指標,只複製其位址。
深複製: 遇到成員指標,複製整個物件(在該物件中,設計Clone並繼承prototype class)
className(const className& myself)  //className自己的建構子
{
    //other member var
    ptr = myself.ptr->Clone()// 深層複製
}
className* Clone()  //className是自己類別的類別名稱
{
    return new className(*this); // new一份自己(物件)回傳出去
}

樣版模式

實作注意:將共同的部份放到父類別,特別的部份放到子類別。透過父類別中public的function call back子類別中private的function
class classBasis
{
    virtual void Detail() = 0;  //子類別要實作的部份
public:
    void template()
    {
        /*do some thing*/
        Detail();
    }
};
class classDerivative : public classBasis
{
    void Detail(){}  //設為private
};
FunctionDeri()不可以直接creat子類別的物件,再直接呼叫,一定是被call back的function。

外觀模式

簡化API,隱藏複雜度,將各種類別放到一個類別裡面,透過簡化的fucntion呼叫複雜或多個function,達到簡化的作用;換句話說,外觀模式是建立一個class擁有許多class。當多個function符合概念整體性,就建造一個外觀。

建造者模式

將建造條件與建造細節分開。
  • 建造條件
    為預防漏掉任何建造條件,利用純虛擬函數強迫檢查是否全部必要的建造條件都有(在衍生類別)覆寫。(有點像工廠方法)
  • 建造細節
  • 繼承builder這個純虛擬類別的衍生類別。
  • 建造過程
    擁有將建造條件的純虛擬,簡化介面呼叫(用一個function呼叫很多function)
//建造條件
class builder
{
/*constructor or other function*/
public:
    virtual void functionA() = 0;
    virtual void functionB() = 0;
    virtual void functionC() = 0;
};
//建造細節
class builderForTarget
{
public:
    void functionA(){}; //定義建造細節
    void functionB(){};
    void functionC(){};
};
//建造過程
class directory
{
    builder* m_builder;  //子類別要實作的部份
public:
    void function()
    {
        m_builder->functionA();
        m_builder->functionB();
        m_builder->functionC();
    }
};

觀察者模式

做為雙向耦藕合的兩個類別,解耦之用,程式碼如下。
class B的Update()一定要用一個B.cpp裝起來,如果放在B.h的話,call A::Call()會找不到定義。
這兩個類別彼此互耦,彼此的關係就是B等A呼叫而跟著更新訊息,所以稱B為觀察者。
//a.h
#include <vector>
#include "bbb.h"
class A
{
    std::vector<B*> vb;
public:
    void Call()
    {
        for (std::vector<B*>::iterator it = vb.begin(); it != vb.end(); ++it)
            (*it)->Update();
    }
};
//b.h
#include <iostream>
class A;
class B
{
    A* a;
public:
    void Update();
};
//b.cpp
#include "aaa.h"
#include "bbb.h"
void B::Update(){  a->Call(); }
程式碼可以解耦成這樣,讓介面與衍生類別耦合。衍生類別透過virtual,可以在定義中操作基礎類別當作是衍生類別。
//a0.h
#include "b0.h"
struct A0
{
    virtual void Add(B0* b) = 0;
    virtual void Call() = 0;
};
//aaa.h
#include <vector>
#include "a0.h"
class A : public A0
{
    std::vector<B0*> vb;
public:
    void Add(B0* b)
    { vb.push_back(b); }
    void Call()
    {
        for (std::vector<B0*>::iterator it = vb.begin(); it != vb.end(); ++it)
            (*it)->Update();
    }
};
//b0.h
struct B0
{
    virtual void Update() = 0;
};
//bbb.h
class B : public B0
{
    A* a;
public:
    void Update()
    { a->Call(); };
};
這樣的實作方式,常用在
Client/Server
Document/View
這種一個地方修改,其它看見該文件的view都要跟著更新的程式碼。
也可以說是重覆利用「call一個function,這個function會call其它function的一種機制」。

抽象工廠模式

簡單工廠模式: 用變數建立物件。
工廠方法模式: 用介面建立物件(讓子類別決定建立的物件。)
抽象工廠模式: 用介面建立介面(讓子類別決定建立的介面,讓介面操作物件。)
//用這個決定物件是誰(不過,若要使用switch-case就...和簡單工廠一樣了XD
    //iFactory* factory = new SqlSevFactory();
    iFactory* factory = new AccessFactory();
    
    iUser* iu = factory->createUser();
    User* user = new User();
    iu->Insert(user);
    iu->getUser(1);

    iDepartment* id = factory->creatDepartment();
    Department* dept = new Department();
    id->Insert(dept);
    id->getDeptName(1);

狀態模式

實作注意:將if-else換成一個State類別動態連結的function call,該function定義了各種不同的State的決策(邏輯)內容。
function裡描述「狀態的轉換」總是用長長的if-else。和FSM相同的特色在於「目前狀態會決定前往特定的下一個狀態」,不同的地方在於「輸出和狀態有關,還是和輸入有關,在此不重要」。要設計成邏輯與運算分離,邏輯的function就是狀態模式,容易變動的部份就是增減狀態的部份
#ifndef STATE_H
#define STATE_H

class Something;  //原本有很長的if-else的class

class State
{
public:
 virtual void UpdateState(Something& w) = 0;
};
#endif

轉接器模式

和proxy代理模式非常的像
  • 轉接器模式「轉接與被轉接,不需要衍生自同一個基礎類別」
  • 代理模式「代理與被代理,都需要洐生自同一個基礎類別」
使用時機,能不用就不用,萬一要用的時候,就是沒救的時候了。算是最後一招了!

備忘錄模式
組合模式
迭代器模式
獨體模式

橋接模式

將兩種可形成組識的獨立概念拆開設計。
並且可以自由組合成所有排列組合的結果。
就像是模組化設計、再組合起來的感覺。

命令模式

用基礎類別指標指向衍生類別物件,將裝著基礎類別指標容器的增加、減少,透過基礎類別的function界面,呼叫真實執行的函數。
最後利用動態連結的特性,用一個迴圈一次執行。
在增加減少的過程,可以將衍生類別加入容器中。
將容器與這一系列的動作包成Command類別,也可以隱藏指標在容器中的複雜度

責任鍊模式

沒有留言:

張貼留言

(什麼是留言欄訊息?)