Design Pattern 小筆記

  1. Design Pattern 小筆記
    1. 模式名稱
    2. 簡單工廠模式
    3. 策略模式
    4. 裝飾模式
    5. 代理模式
    6. 工廠方法模式
    7. 原型模式
    8. 樣版模式
    9. 外觀模式
    10. 建造者模式
    11. 觀察者模式
    12. 抽象工廠模式
    13. 狀態模式
    14. 轉接器模式
    15. 橋接模式
    16. 命令模式

Design Pattern 小筆記

原文連結: https://darkblack01.blogspot.com/2013/12/design-pattern.html
移植時的最後更新日期: 2016-04-11T16:17:29.998+08:00

所有的練習我有開一個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類別,也可以隱藏指標在容器中的複雜度

責任鍊模式