C++ 程式設計:程式碼模式設計
軟體設計模式是幫助構建系統設計的抽象。雖然不是新的,因為這個概念已經被克里斯托弗·亞歷山大在他的建築理論中描述過,它只是由於設計模式:可重用面向物件軟體的元素一書在 1994 年 10 月由Erich Gamma、Richard Helm、Ralph Johnson 和John Vlissides出版,被稱為四人幫 (GoF),該書識別並描述了 23 種經典軟體設計模式。
設計模式既不是靜態解決方案,也不是演算法。模式是一種描述和命名重複解決方案或方法來解決常見設計問題的方法,也就是說,一種解決通用問題的常見方法(模式的通用性或特定性取決於目標的限制程度)。模式可以自發產生或設計產生。這就是為什麼設計模式作為實現的抽象和設計階段的幫助而有用。有了這個概念,提供了一種更容易促進設計選擇溝通的方法,作為規範化技術,以便每個人都可以分享設計理念。
根據它們解決的設計問題,設計模式可以分為不同的類別,其中主要類別是
模式通常存在於面向物件的程式語言(如 C++ 或 Java)中。它們可以被視為解決在許多不同情況或應用程式中出現的問題的模板。它不是程式碼重用,因為它通常不指定程式碼,但程式碼可以很容易地從設計模式建立。面向物件的設計模式通常顯示類或物件之間的關係和互動,而不指定最終涉及的應用程式類或物件。
每個設計模式都包含以下部分
- 問題/需求
- 要使用設計模式,我們需要進行一個可能被編碼以測試解決方案的迷你分析設計。本節說明我們要解決的問題的需求。這通常是一個常見問題,將在多個應用程式中出現。
- 力量
- 本節說明技術邊界,這些邊界有助於並指導解決方案的建立。
- 解決方案
- 本節描述如何編寫程式碼來解決上述問題。這是設計模式的設計部分。它可能包含類圖、時序圖,或任何其他描述如何編碼解決方案所需的內容。
設計模式可以被認為是解決特定設計問題的普遍認可的最佳實踐的標準化。應該將它們理解為在應用程式中實施良好設計模式的方法。這樣做將減少使用低效和模糊的解決方案。使用設計模式可以加快您的設計速度,並幫助您將其傳達給其他程式設計師。
在軟體工程中,建立設計模式是處理物件建立機制的設計模式,試圖以適合情況的方式建立物件。物件的建立基本形式會導致設計問題或增加設計複雜性。建立設計模式透過某種方式控制此物件建立來解決此問題。
在本節中,我們假設讀者已經熟悉函式、全域性變數、棧與堆、類、指標和靜態成員函式,這些內容在之前已經介紹過。
正如我們將會看到的那樣,有幾種建立設計模式,它們都將處理特定的實現任務,這將為程式碼庫建立更高的抽象級別,現在我們將分別介紹每一種。
建造者建立模式用於將複雜物件的構建與其表示分離,以便同一個構建過程可以建立不同的物件表示。
- 問題
- 我們想要構建一個複雜的物件,但是我們不想有一個複雜的建構函式成員,或者一個需要很多引數的建構函式成員。
- 解決方案
- 定義一箇中間物件,其成員函式在物件可供客戶端使用之前按部分定義所需的物件。建造者模式允許我們在指定所有建立選項之前推遲物件的構建。
#include <string>
#include <iostream>
#include <memory>
using namespace std;
// "Product"
class Pizza
{
public:
void setDough(const string& dough)
{
m_dough = dough;
}
void setSauce(const string& sauce)
{
m_sauce = sauce;
}
void setTopping(const string& topping)
{
m_topping = topping;
}
void open() const
{
cout << "Pizza with " << m_dough << " dough, " << m_sauce << " sauce and "
<< m_topping << " topping. Mmm." << endl;
}
private:
string m_dough;
string m_sauce;
string m_topping;
};
// "Abstract Builder"
class PizzaBuilder
{
public:
virtual ~PizzaBuilder() {};
Pizza* getPizza()
{
return m_pizza.get();
}
void createNewPizzaProduct()
{
m_pizza = make_unique<Pizza>();
}
virtual void buildDough() = 0;
virtual void buildSauce() = 0;
virtual void buildTopping() = 0;
protected:
unique_ptr<Pizza> m_pizza;
};
//----------------------------------------------------------------
class HawaiianPizzaBuilder : public PizzaBuilder
{
public:
virtual ~HawaiianPizzaBuilder() {};
virtual void buildDough()
{
m_pizza->setDough("cross");
}
virtual void buildSauce()
{
m_pizza->setSauce("mild");
}
virtual void buildTopping()
{
m_pizza->setTopping("ham+pineapple");
}
};
class SpicyPizzaBuilder : public PizzaBuilder
{
public:
virtual ~SpicyPizzaBuilder() {};
virtual void buildDough()
{
m_pizza->setDough("pan baked");
}
virtual void buildSauce()
{
m_pizza->setSauce("hot");
}
virtual void buildTopping()
{
m_pizza->setTopping("pepperoni+salami");
}
};
//----------------------------------------------------------------
class Cook
{
public:
void openPizza()
{
m_pizzaBuilder->getPizza()->open();
}
void makePizza(PizzaBuilder* pb)
{
m_pizzaBuilder = pb;
m_pizzaBuilder->createNewPizzaProduct();
m_pizzaBuilder->buildDough();
m_pizzaBuilder->buildSauce();
m_pizzaBuilder->buildTopping();
}
private:
PizzaBuilder* m_pizzaBuilder;
};
int main()
{
Cook cook;
HawaiianPizzaBuilder hawaiianPizzaBuilder;
SpicyPizzaBuilder spicyPizzaBuilder;
cook.makePizza(&hawaiianPizzaBuilder);
cook.openPizza();
cook.makePizza(&spicyPizzaBuilder);
cook.openPizza();
}
你也可以使用最新的 c++17 標準
#include <iostream>
#include <memory>
class Pizza{
public:
void setDough(const std::string& dough){
m_dough = dough;
}
void setSauce(const std::string& sauce){
m_sauce = sauce;
}
void setTopping(const std::string& topping){
m_topping = topping;
}
void open() const {
std::cout<<"The Pizza have "<<
m_dough<<" dough, "<<
m_sauce<<" sauce, "<<
m_topping<<" topping."<<
std::endl;
}
private:
std::string m_dough;
std::string m_sauce;
std::string m_topping;
};
class PizzaBuilder{
public:
virtual ~PizzaBuilder() = default;
void createNewPizza(){
m_pizza = std::make_unique<Pizza>();
}
Pizza* getPizza() {
return m_pizza.release();
}
virtual void buildDough() = 0;
virtual void buildSauce() = 0;
virtual void buildTopping() = 0;
protected:
std::unique_ptr<Pizza> m_pizza;
};
class HawaiianPizzaBuilder:public PizzaBuilder{
public:
~HawaiianPizzaBuilder() override = default;
void buildDough() override {
m_pizza->setDough("Hawaiian dough");
}
void buildSauce() override {
m_pizza->setSauce("Hawaiian sauce");
}
void buildTopping() override {
m_pizza->setTopping("Hawaiian topping");
}
};
class SpicyPizzaBuilder:public PizzaBuilder{
public:
~SpicyPizzaBuilder() override = default;
void buildDough() override {
m_pizza->setDough("Spicy dough");
}
void buildSauce() override {
m_pizza->setSauce("Spicy sauce");
}
void buildTopping() override {
m_pizza->setTopping("Spicy topping");
}
};
class Cook{
public:
void openPizza() const {
m_pizzaBuilder->getPizza()->open();
}
void createPizza(PizzaBuilder* pizzaBuilder){
m_pizzaBuilder = pizzaBuilder;
m_pizzaBuilder->createNewPizza();
m_pizzaBuilder->buildDough();
m_pizzaBuilder->buildSauce();
m_pizzaBuilder->buildTopping();
}
private:
PizzaBuilder* m_pizzaBuilder;
};
int main(){
Cook cook{};
HawaiianPizzaBuilder hawaiianPizzaBuilder;
cook.createPizza(&hawaiianPizzaBuilder);
cook.openPizza();
SpicyPizzaBuilder spicyPizzaBuilder;
cook.createPizza(&spicyPizzaBuilder);
cook.openPizza();
}
//console output
//The Pizza have Hawaiian dough dough, Hawaiian sauce sauce, Hawaiian topping topping.
//The Pizza have Spicy dough dough, Spicy sauce sauce, Spicy topping topping.
定義:一個實用類,從派生類族中建立一個類的例項。
定義:一個實用類,建立多個類族例項。它還可以返回特定組的工廠。
工廠設計模式在需要建立許多不同型別物件(所有物件都從一個共同的基型別派生)的情況下很有用。工廠方法定義了一個建立物件的方法,子類可以覆蓋該方法以指定要建立的派生型別。因此,在執行時,工廠方法可以傳遞所需物件的描述(例如,從使用者輸入中讀取的字串)並返回指向該物件的新例項的基類指標。當為基類使用精心設計介面時,該模式效果最佳,因此無需強制轉換返回的物件。
- 問題
- 我們想要在執行時根據某些配置或應用程式引數決定要建立哪個物件。當我們編寫程式碼時,我們不知道應該例項化哪個類。
- 解決方案
- 定義一個用於建立物件的介面,但讓子類決定要例項化哪個類。工廠方法讓一個類將例項化推遲到子類。
在以下示例中,工廠方法用於在執行時建立筆記型電腦或臺式電腦物件。
讓我們從定義Computer開始,這是一個抽象基類(介面)及其派生類:Laptop和Desktop。
class Computer
{
public:
virtual void Run() = 0;
virtual void Stop() = 0;
virtual ~Computer() {}; /* without this, you do not call Laptop or Desktop destructor in this example! */
};
class Laptop: public Computer
{
public:
void Run() override {mHibernating = false;};
void Stop() override {mHibernating = true;};
virtual ~Laptop() {}; /* because we have virtual functions, we need virtual destructor */
private:
bool mHibernating; // Whether or not the machine is hibernating
};
class Desktop: public Computer
{
public:
void Run() override {mOn = true;};
void Stop() override {mOn = false;};
virtual ~Desktop() {};
private:
bool mOn; // Whether or not the machine has been turned on
};
實際的ComputerFactory類返回一個Computer,給定物件的現實世界描述。
class ComputerFactory
{
public:
static Computer *NewComputer(const std::string &description)
{
if(description == "laptop")
return new Laptop;
if(description == "desktop")
return new Desktop;
return nullptr;
}
};
讓我們分析一下這種設計的優點。首先,有一個編譯方面的優點。如果我們將介面Computer與工廠一起移動到一個單獨的標頭檔案中,我們就可以將NewComputer()函式的實現移動到一個單獨的實現檔案中。現在NewComputer()的實現檔案是唯一需要了解派生類的檔案。因此,如果對Computer的任何派生類進行了更改,或者添加了一個新的Computer子型別,則只需要重新編譯NewComputer()的實現檔案。所有使用工廠的人只關心介面,而介面應該在應用程式的整個生命週期中保持一致。
此外,如果需要新增一個類,並且使用者透過使用者介面請求物件,那麼可能不需要更改呼叫工廠的任何程式碼來支援額外的計算機型別。使用工廠的程式碼只需將新字串傳遞給工廠,並讓工廠完全處理新的型別。
想象一下,你正在編寫一個電子遊戲,你希望將來新增新的敵人型別,每個敵人都有不同的 AI 功能,並且可以以不同的方式更新。透過使用工廠方法,程式的控制器可以呼叫工廠來建立敵人,而無需依賴或瞭解敵人的實際型別。現在,未來的開發人員可以建立新的敵人,使用新的 AI 控制和新的繪圖成員函式,將其新增到工廠,並建立一個呼叫工廠的關卡,並按名稱請求敵人。將此方法與XML關卡描述結合起來,開發人員可以建立新關卡,而無需重新編譯他們的程式。所有這一切,都要歸功於將物件的建立與物件的用法分離。
另一個例子
#include <stdexcept>
#include <iostream>
#include <memory>
using namespace std;
class Pizza {
public:
virtual int getPrice() const = 0;
virtual ~Pizza() {}; /* without this, no destructor for derived Pizza's will be called. */
};
class HamAndMushroomPizza : public Pizza {
public:
virtual int getPrice() const { return 850; };
virtual ~HamAndMushroomPizza() {};
};
class DeluxePizza : public Pizza {
public:
virtual int getPrice() const { return 1050; };
virtual ~DeluxePizza() {};
};
class HawaiianPizza : public Pizza {
public:
virtual int getPrice() const { return 1150; };
virtual ~HawaiianPizza() {};
};
class PizzaFactory {
public:
enum PizzaType {
HamMushroom,
Deluxe,
Hawaiian
};
static unique_ptr<Pizza> createPizza(PizzaType pizzaType) {
switch (pizzaType) {
case HamMushroom: return make_unique<HamAndMushroomPizza>();
case Deluxe: return make_unique<DeluxePizza>();
case Hawaiian: return make_unique<HawaiianPizza>();
}
throw "invalid pizza type.";
}
};
/*
* Create all available pizzas and print their prices
*/
void pizza_information(PizzaFactory::PizzaType pizzatype)
{
unique_ptr<Pizza> pizza = PizzaFactory::createPizza(pizzatype);
cout << "Price of " << pizzatype << " is " << pizza->getPrice() << std::endl;
}
int main()
{
pizza_information(PizzaFactory::HamMushroom);
pizza_information(PizzaFactory::Deluxe);
pizza_information(PizzaFactory::Hawaiian);
}
原型
[edit | edit source]當要建立的物件型別由原型例項確定時,原型模式用於軟體開發中,原型例項被克隆以生成新物件。例如,當以標準方式(例如,使用new關鍵字)建立新物件的固有成本對於給定應用程式來說過高時,使用此模式。
實現:宣告一個抽象基類,該類指定一個純虛擬clone()方法。任何需要“多型建構函式”功能的類都從抽象基類派生,並實現clone()操作。
這裡,客戶端程式碼首先呼叫工廠方法。此工廠方法根據引數找出具體的類。在這個具體的類上,呼叫clone()方法,並由工廠方法返回物件。
- 這是一個原型方法的示例實現。這裡我們對所有元件都有詳細的描述。
/** Implementation of Prototype Method **/
#include <iostream>
#include <unordered_map>
#include <string>
#include <memory>
using namespace std;
/** Record is the base Prototype */
class Record
{
public:
virtual ~Record() {}
virtual void print() = 0;
virtual unique_ptr<Record> clone() = 0;
};
/** CarRecord is a Concrete Prototype */
class CarRecord : public Record
{
private:
string m_carName;
int m_ID;
public:
CarRecord(string carName, int ID) : m_carName(carName), m_ID(ID)
{
}
void print() override
{
cout << "Car Record" << endl
<< "Name : " << m_carName << endl
<< "Number: " << m_ID << endl << endl;
}
unique_ptr<Record> clone() override
{
return make_unique<CarRecord>(*this);
}
};
/** BikeRecord is the Concrete Prototype */
class BikeRecord : public Record
{
private:
string m_bikeName;
int m_ID;
public:
BikeRecord(string bikeName, int ID) : m_bikeName(bikeName), m_ID(ID)
{
}
void print() override
{
cout << "Bike Record" << endl
<< "Name : " << m_bikeName << endl
<< "Number: " << m_ID << endl << endl;
}
unique_ptr<Record> clone() override
{
return make_unique<BikeRecord>(*this);
}
};
/** PersonRecord is the Concrete Prototype */
class PersonRecord : public Record
{
private:
string m_personName;
int m_age;
public:
PersonRecord(string personName, int age) : m_personName(personName), m_age(age)
{
}
void print() override
{
cout << "Person Record" << endl
<< "Name : " << m_personName << endl
<< "Age : " << m_age << endl << endl;
}
unique_ptr<Record> clone() override
{
return make_unique<PersonRecord>(*this);
}
};
/** Opaque record type, avoids exposing concrete implementations */
enum RecordType
{
CAR,
BIKE,
PERSON
};
/** RecordFactory is the client */
class RecordFactory
{
private:
unordered_map<RecordType, unique_ptr<Record>, hash<int> > m_records;
public:
RecordFactory()
{
m_records[CAR] = make_unique<CarRecord>("Ferrari", 5050);
m_records[BIKE] = make_unique<BikeRecord>("Yamaha", 2525);
m_records[PERSON] = make_unique<PersonRecord>("Tom", 25);
}
unique_ptr<Record> createRecord(RecordType recordType)
{
return m_records[recordType]->clone();
}
};
int main()
{
RecordFactory recordFactory;
auto record = recordFactory.createRecord(CAR);
record->print();
record = recordFactory.createRecord(BIKE);
record->print();
record = recordFactory.createRecord(PERSON);
record->print();
}
另一個例子
要實現該模式,請宣告一個抽象基類,該類指定一個純虛擬clone()成員函式。任何需要“多型建構函式”功能的類都從抽象基類派生,並實現clone()操作。
客戶端,而不是編寫呼叫new運算子的程式碼來處理硬編碼的類名,而是呼叫原型上的clone()成員函式,呼叫帶有指定所需特定具體派生類的引數的工廠成員函式,或透過另一個設計模式提供的某些機制來呼叫clone()成員函式。
class CPrototypeMonster
{
protected:
CString _name;
public:
CPrototypeMonster();
CPrototypeMonster( const CPrototypeMonster& copy );
virtual ~CPrototypeMonster();
virtual CPrototypeMonster* Clone() const=0; // This forces every derived class to provide an override for this function.
void Name( CString name );
CString Name() const;
};
class CGreenMonster : public CPrototypeMonster
{
protected:
int _numberOfArms;
double _slimeAvailable;
public:
CGreenMonster();
CGreenMonster( const CGreenMonster& copy );
~CGreenMonster();
virtual CPrototypeMonster* Clone() const;
void NumberOfArms( int numberOfArms );
void SlimeAvailable( double slimeAvailable );
int NumberOfArms() const;
double SlimeAvailable() const;
};
class CPurpleMonster : public CPrototypeMonster
{
protected:
int _intensityOfBadBreath;
double _lengthOfWhiplikeAntenna;
public:
CPurpleMonster();
CPurpleMonster( const CPurpleMonster& copy );
~CPurpleMonster();
virtual CPrototypeMonster* Clone() const;
void IntensityOfBadBreath( int intensityOfBadBreath );
void LengthOfWhiplikeAntenna( double lengthOfWhiplikeAntenna );
int IntensityOfBadBreath() const;
double LengthOfWhiplikeAntenna() const;
};
class CBellyMonster : public CPrototypeMonster
{
protected:
double _roomAvailableInBelly;
public:
CBellyMonster();
CBellyMonster( const CBellyMonster& copy );
~CBellyMonster();
virtual CPrototypeMonster* Clone() const;
void RoomAvailableInBelly( double roomAvailableInBelly );
double RoomAvailableInBelly() const;
};
CPrototypeMonster* CGreenMonster::Clone() const
{
return new CGreenMonster(*this);
}
CPrototypeMonster* CPurpleMonster::Clone() const
{
return new CPurpleMonster(*this);
}
CPrototypeMonster* CBellyMonster::Clone() const
{
return new CBellyMonster(*this);
}
一個具體怪物類別的客戶端只需要一個對CPrototypeMonster類物件的引用(指標)即可呼叫“Clone”函式並建立該物件的副本。下面的函式演示了這個概念
void DoSomeStuffWithAMonster( const CPrototypeMonster* originalMonster )
{
CPrototypeMonster* newMonster = originalMonster->Clone();
ASSERT( newMonster );
newMonster->Name("MyOwnMonster");
// Add code doing all sorts of cool stuff with the monster.
delete newMonster;
}
現在,originalMonster可以作為指向CGreenMonster、CPurpleMonster或CBellyMonster的指標傳遞。
單例
[edit | edit source]單例模式確保一個類只有一個例項,並提供對該例項的全域性訪問點。它以單例集命名,單例集被定義為包含一個元素的集合。這在需要一個物件來協調整個系統的操作時很有用。
檢查清單
- 在“單例”類中定義一個私有靜態屬性。
- 在類中定義一個公有靜態訪問器函式。
- 在訪問器函式中執行“延遲初始化”(首次使用時建立)。
- 定義所有建構函式為受保護或私有。
- 客戶端只能使用訪問器函式來操作單例。
讓我們看看單例與其他變數型別的區別。
與全域性變數一樣,單例存在於任何函式的範圍之外。傳統的實現使用單例類的靜態成員函式,該函式將在第一次呼叫時建立一個單例類的單個例項,並永遠返回該例項。下面的程式碼示例說明了 C++ 單例類的元素,它只是儲存一個字串。
class StringSingleton
{
public:
// Some accessor functions for the class, itself
std::string GetString() const
{return mString;}
void SetString(const std::string &newStr)
{mString = newStr;}
// The magic function, which allows access to the class from anywhere
// To get the value of the instance of the class, call:
// StringSingleton::Instance().GetString();
static StringSingleton &Instance()
{
// This line only runs once, thus creating the only instance in existence
static std::auto_ptr<StringSingleton> instance( new StringSingleton );
// dereferencing the variable here, saves the caller from having to use
// the arrow operator, and removes temptation to try and delete the
// returned instance.
return *instance; // always returns the same instance
}
private:
// We need to make some given functions private to finish the definition of the singleton
StringSingleton(){} // default constructor available only to members or friends of this class
// Note that the next two functions are not given bodies, thus any attempt
// to call them implicitly will return as compiler errors. This prevents
// accidental copying of the only instance of the class.
StringSingleton(const StringSingleton &old); // disallow copy constructor
const StringSingleton &operator=(const StringSingleton &old); //disallow assignment operator
// Note that although this should be allowed,
// some compilers may not implement private destructors
// This prevents others from deleting our one single instance, which was otherwise created on the heap
~StringSingleton(){}
private: // private data for an instance of this class
std::string mString;
};
單例的變體
單例類的應用
單例設計模式的一種常見用法是用於應用程式配置。配置可能需要全域性訪問,並且可能需要對應用程式配置進行未來的擴充套件。C 的最接近的替代方案是建立一個全域性struct。這缺乏關於此物件在何處例項化的清晰度,並且不保證物件的存在。
例如,考慮另一個開發人員在他們物件的建構函式中使用你的單例的情況。然後,另一個開發人員決定在全域性範圍內建立一個第二個類的例項。如果你只是使用了一個全域性變數,那麼連結的順序就會很重要。由於你的全域性變數將被訪問,可能在主函式開始執行之前,沒有關於全域性變數是否被初始化或第二個類的建構函式是否首先被呼叫的定義。這種行為可能會隨著對程式碼其他區域的細微修改而改變,這將改變全域性程式碼執行的順序。這種錯誤很難除錯。但是,使用單例,第一次訪問物件時,也會建立物件。現在你有一個物件,它將始終存在,與被使用相關,並且如果從未使用過,它將永遠不存在。
此類的第二個常見用途是在更新舊程式碼以在新的體系結構中工作。由於開發人員可能廣泛使用了全域性變數,因此將它們移入一個類並使其成為單例,可以作為將程式與更強大的面向物件結構保持一致的中間步驟。
另一個例子
#include <iostream>
using namespace std;
/* Place holder for thread synchronization mutex */
class Mutex
{ /* placeholder for code to create, use, and free a mutex */
};
/* Place holder for thread synchronization lock */
class Lock
{ public:
Lock(Mutex& m) : mutex(m) { /* placeholder code to acquire the mutex */ }
~Lock() { /* placeholder code to release the mutex */ }
private:
Mutex & mutex;
};
class Singleton
{ public:
static Singleton* GetInstance();
int a;
~Singleton() { cout << "In Destructor" << endl; }
private:
Singleton(int _a) : a(_a) { cout << "In Constructor" << endl; }
static Mutex mutex;
// Not defined, to prevent copying
Singleton(const Singleton& );
Singleton& operator =(const Singleton& other);
};
Mutex Singleton::mutex;
Singleton* Singleton::GetInstance()
{
Lock lock(mutex);
cout << "Get Instance" << endl;
// Initialized during first access
static Singleton inst(1);
return &inst;
}
int main()
{
Singleton* singleton = Singleton::GetInstance();
cout << "The value of the singleton: " << singleton->a << endl;
return 0;
}
結構模式
[edit | edit source]介面卡
[edit | edit source]將一個類的介面轉換為客戶端期望的另一個介面。介面卡使那些由於介面不相容而無法協同工作的類能夠協同工作。
#include <iostream>
class Dog { // Abstract Target
public:
virtual ~Dog() = default;
virtual void performsConversion() const = 0;
};
class DogFemale : public Dog { // Concrete Target
public:
virtual void performsConversion() const override { std::cout << "Dog female performs conversion." << std::endl; }
};
class Cat { // Abstract Adaptee
public:
virtual ~Cat() = default;
virtual void performsConversion() const = 0;
};
class CatFemale : public Cat { // Concrete Adaptee
public:
virtual void performsConversion() const override { std::cout << "Cat female performs conversion." << std::endl; }
};
class DogNature {
public:
void carryOutNature(Dog* dog) {
std::cout << "On with the Dog nature!" << std::endl;
dog->performsConversion();
}
};
class ConversionAdapter : public Dog { // Adapter
private:
Cat* cat;
public:
ConversionAdapter(Cat* c) : cat(c) {}
virtual void performsConversion() const override { cat->performsConversion(); }
};
int main() { // Client code
DogFemale* dogFemale = new DogFemale;
CatFemale* catFemale = new CatFemale;
DogNature dogNature;
// dogNature.carryOutNature (catFemale); // Will not compile of course since the parameter must be of type Dog*.
ConversionAdapter* adaptedCat = new ConversionAdapter(catFemale); // catFemale has adapted to become a Dog!
dogNature.carryOutNature(dogFemale);
dogNature.carryOutNature(adaptedCat); // So now catFemale, in the form of adaptedCat, participates in the dogNature!
// Note that catFemale is carrying out her own type of nature in dogNature though.
delete adaptedCat; // adaptedCat is not needed anymore
delete catFemale; // catFemale is not needed anymore
delete dogFemale; // dogFemale is not needed anymore, too
return 0;
}
橋接
[edit | edit source]橋接模式用於將介面與其實現分離。這樣做提供了靈活性,使兩者都可以獨立變化。
以下示例將輸出
API1.circle at 1:2 7.5 API2.circle at 5:7 27.5
#include <iostream>
using namespace std;
/* Implementor*/
class DrawingAPI {
public:
virtual void drawCircle(double x, double y, double radius) = 0;
virtual ~DrawingAPI() {}
};
/* Concrete ImplementorA*/
class DrawingAPI1 : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) {
cout << "API1.circle at " << x << ':' << y << ' ' << radius << endl;
}
};
/* Concrete ImplementorB*/
class DrawingAPI2 : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) {
cout << "API2.circle at " << x << ':' << y << ' ' << radius << endl;
}
};
/* Abstraction*/
class Shape {
public:
virtual ~Shape() {}
virtual void draw() = 0;
virtual void resizeByPercentage(double pct) = 0;
};
/* Refined Abstraction*/
class CircleShape : public Shape {
public:
CircleShape(double x, double y,double radius, DrawingAPI *drawingAPI) :
m_x(x), m_y(y), m_radius(radius), m_drawingAPI(drawingAPI)
{}
void draw() {
m_drawingAPI->drawCircle(m_x, m_y, m_radius);
}
void resizeByPercentage(double pct) {
m_radius *= pct;
}
private:
double m_x, m_y, m_radius;
DrawingAPI *m_drawingAPI;
};
int main(void) {
CircleShape circle1(1,2,3,new DrawingAPI1());
CircleShape circle2(5,7,11,new DrawingAPI2());
circle1.resizeByPercentage(2.5);
circle2.resizeByPercentage(2.5);
circle1.draw();
circle2.draw();
return 0;
}
組合
[edit | edit source]組合使客戶端可以統一地對待單個物件和物件的組合。組合模式可以表示這兩種情況。在這個模式中,可以開發樹結構來表示部分-整體層次結構。
#include <vector>
#include <iostream> // std::cout
#include <memory> // std::auto_ptr
#include <algorithm> // std::for_each
using namespace std;
class Graphic
{
public:
virtual void print() const = 0;
virtual ~Graphic() {}
};
class Ellipse : public Graphic
{
public:
void print() const {
cout << "Ellipse \n";
}
};
class CompositeGraphic : public Graphic
{
public:
void print() const {
for(Graphic * a: graphicList_) {
a->print();
}
}
void add(Graphic *aGraphic) {
graphicList_.push_back(aGraphic);
}
private:
vector<Graphic*> graphicList_;
};
int main()
{
// Initialize four ellipses
const auto_ptr<Ellipse> ellipse1(new Ellipse());
const auto_ptr<Ellipse> ellipse2(new Ellipse());
const auto_ptr<Ellipse> ellipse3(new Ellipse());
const auto_ptr<Ellipse> ellipse4(new Ellipse());
// Initialize three composite graphics
const auto_ptr<CompositeGraphic> graphic(new CompositeGraphic());
const auto_ptr<CompositeGraphic> graphic1(new CompositeGraphic());
const auto_ptr<CompositeGraphic> graphic2(new CompositeGraphic());
// Composes the graphics
graphic1->add(ellipse1.get());
graphic1->add(ellipse2.get());
graphic1->add(ellipse3.get());
graphic2->add(ellipse4.get());
graphic->add(graphic1.get());
graphic->add(graphic2.get());
// Prints the complete graphic (four times the string "Ellipse")
graphic->print();
return 0;
}
裝飾器
[edit | edit source]裝飾器模式有助於動態地向物件附加額外的行為或職責。裝飾器為擴充套件功能提供了一種靈活的替代子類化的方案。這也稱為“包裝器”。如果你的應用程式進行某種過濾,那麼裝飾器可能是考慮用於此任務的好模式。
#include <string>
#include <iostream>
using namespace std;
class Car //Our Abstract base class
{
protected:
string _str;
public:
Car()
{
_str = "Unknown Car";
}
virtual string getDescription()
{
return _str;
}
virtual double getCost() = 0;
virtual ~Car()
{
cout << "~Car()\n";
}
};
class OptionsDecorator : public Car //Decorator Base class
{
public:
virtual string getDescription() = 0;
virtual ~OptionsDecorator()
{
cout<<"~OptionsDecorator()\n";
}
};
class CarModel1 : public Car
{
public:
CarModel1()
{
_str = "CarModel1";
}
virtual double getCost()
{
return 31000.23;
}
~CarModel1()
{
cout<<"~CarModel1()\n";
}
};
class Navigation: public OptionsDecorator
{
Car *_b;
public:
Navigation(Car *b)
{
_b = b;
}
string getDescription()
{
return _b->getDescription() + ", Navigation";
}
double getCost()
{
return 300.56 + _b->getCost();
}
~Navigation()
{
cout << "~Navigation()\n";
delete _b;
}
};
class PremiumSoundSystem: public OptionsDecorator
{
Car *_b;
public:
PremiumSoundSystem(Car *b)
{
_b = b;
}
string getDescription()
{
return _b->getDescription() + ", PremiumSoundSystem";
}
double getCost()
{
return 0.30 + _b->getCost();
}
~PremiumSoundSystem()
{
cout << "~PremiumSoundSystem()\n";
delete _b;
}
};
class ManualTransmission: public OptionsDecorator
{
Car *_b;
public:
ManualTransmission(Car *b)
{
_b = b;
}
string getDescription()
{
return _b->getDescription()+ ", ManualTransmission";
}
double getCost()
{
return 0.30 + _b->getCost();
}
~ManualTransmission()
{
cout << "~ManualTransmission()\n";
delete _b;
}
};
int main()
{
//Create our Car that we want to buy
Car *b = new CarModel1();
cout << "Base model of " << b->getDescription() << " costs $" << b->getCost() << "\n";
//Who wants base model let's add some more features
b = new Navigation(b);
cout << b->getDescription() << " will cost you $" << b->getCost() << "\n";
b = new PremiumSoundSystem(b);
b = new ManualTransmission(b);
cout << b->getDescription() << " will cost you $" << b->getCost() << "\n";
// WARNING! Here we leak the CarModel1, Navigation and PremiumSoundSystem objects!
// Either we delete them explicitly or rewrite the Decorators to take
// ownership and delete their Cars when destroyed.
delete b;
return 0;
}
上面程式的輸出是
Base model of CarModel1 costs $31000.2
CarModel1, Navigation will cost you $31300.8
CarModel1, Navigation, PremiumSoundSystem, ManualTransmission will cost you $31301.4
~ManualTransmission
~PremiumSoundSystem()
~Navigation()
~CarModel1
~Car()
~OptionsDecorator()
~Car()
~OptionsDecorator()
~Car()
~OptionsDecorator()
~Car()
另一個例子(C++14)
#include <iostream>
#include <string>
#include <memory>
class Interface {
public:
virtual ~Interface() { }
virtual void write (std::string&) = 0;
};
class Core : public Interface {
public:
~Core() {std::cout << "Core destructor called.\n";}
virtual void write (std::string& text) override {}; // Do nothing.
};
class Decorator : public Interface {
private:
std::unique_ptr<Interface> interface;
public:
Decorator (std::unique_ptr<Interface> c) {interface = std::move(c);}
virtual void write (std::string& text) override {interface->write(text);}
};
class MessengerWithSalutation : public Decorator {
private:
std::string salutation;
public:
MessengerWithSalutation (std::unique_ptr<Interface> c, const std::string& str) : Decorator(std::move(c)), salutation(str) {}
~MessengerWithSalutation() {std::cout << "Messenger destructor called.\n";}
virtual void write (std::string& text) override {
text = salutation + "\n\n" + text;
Decorator::write(text);
}
};
class MessengerWithValediction : public Decorator {
private:
std::string valediction;
public:
MessengerWithValediction (std::unique_ptr<Interface> c, const std::string& str) : Decorator(std::move(c)), valediction(str) {}
~MessengerWithValediction() {std::cout << "MessengerWithValediction destructor called.\n";}
virtual void write (std::string& text) override {
Decorator::write(text);
text += "\n\n" + valediction;
}
};
int main() {
const std::string salutation = "Greetings,";
const std::string valediction = "Sincerly, Andy";
std::string message1 = "This message is not decorated.";
std::string message2 = "This message is decorated with a salutation.";
std::string message3 = "This message is decorated with a valediction.";
std::string message4 = "This message is decorated with a salutation and a valediction.";
std::unique_ptr<Interface> messenger1 = std::make_unique<Core>();
std::unique_ptr<Interface> messenger2 = std::make_unique<MessengerWithSalutation> (std::make_unique<Core>(), salutation);
std::unique_ptr<Interface> messenger3 = std::make_unique<MessengerWithValediction> (std::make_unique<Core>(), valediction);
std::unique_ptr<Interface> messenger4 = std::make_unique<MessengerWithValediction> (std::make_unique<MessengerWithSalutation>
(std::make_unique<Core>(), salutation), valediction);
messenger1->write(message1);
std::cout << message1 << '\n';
std::cout << "\n------------------------------\n\n";
messenger2->write(message2);
std::cout << message2 << '\n';
std::cout << "\n------------------------------\n\n";
messenger3->write(message3);
std::cout << message3 << '\n';
std::cout << "\n------------------------------\n\n";
messenger4->write(message4);
std::cout << message4 << '\n';
std::cout << "\n------------------------------\n\n";
}
上面程式的輸出是
This message is not decorated.
------------------------------
Greetings,
This message is decorated with a salutation.
------------------------------
This message is decorated with a valediction.
Sincerly, Andy
------------------------------
Greetings,
This message is decorated with a salutation and a valediction.
Sincerly, Andy
------------------------------
MessengerWithValediction destructor called.
Messenger destructor called.
Core destructor called.
MessengerWithValediction destructor called.
Core destructor called.
Messenger destructor called.
Core destructor called.
Core destructor called.
外觀
[edit | edit source]外觀模式透過提供一個統一的介面,隱藏了系統內部的複雜性,允許客戶端透過該介面訪問系統。外觀模式定義了一個更高層次的介面,使得子系統更容易使用。例如,透過呼叫多個其他類的方法,將一個複雜的過程封裝到一個類方法中。
/*Facade is one of the easiest patterns I think... And this is very simple example.
Imagine you set up a smart house where everything is on remote. So to turn the lights on you push lights on button - And same for TV,
AC, Alarm, Music, etc...
When you leave a house you would need to push a 100 buttons to make sure everything is off and are good to go which could be little
annoying if you are lazy like me
so I defined a Facade for leaving and coming back. (Facade functions represent buttons...) So when I come and leave I just make one
call and it takes care of everything...
*/
#include <string>
#include <iostream>
using namespace std;
class Alarm
{
public:
void alarmOn()
{
cout << "Alarm is on and house is secured"<<endl;
}
void alarmOff()
{
cout << "Alarm is off and you can go into the house"<<endl;
}
};
class Ac
{
public:
void acOn()
{
cout << "Ac is on"<<endl;
}
void acOff()
{
cout << "AC is off"<<endl;
}
};
class Tv
{
public:
void tvOn()
{
cout << "Tv is on"<<endl;
}
void tvOff()
{
cout << "TV is off"<<endl;
}
};
class HouseFacade
{
Alarm alarm;
Ac ac;
Tv tv;
public:
HouseFacade(){}
void goToWork()
{
ac.acOff();
tv.tvOff();
alarm.alarmOn();
}
void comeHome()
{
alarm.alarmOff();
ac.acOn();
tv.tvOn();
}
};
int main()
{
HouseFacade hf;
//Rather than calling 100 different on and off functions thanks to facade I only have 2 functions...
hf.goToWork();
hf.comeHome();
}
上面程式的輸出是
AC is off TV is off Alarm is on and house is secured Alarm is off and you can go into the house Ac is on Tv is on
這種模式主要用於透過共享物件的屬性來節省記憶體。想象一下,大量的相似物件,它們的大部分屬性都相同。很自然地,我們將這些屬性從這些物件中移到一個外部資料結構中,併為每個物件提供指向該資料結構的連結。
#include <iostream>
#include <string>
#include <vector>
#define NUMBER_OF_SAME_TYPE_CHARS 3;
/* Actual flyweight objects class (declaration) */
class FlyweightCharacter;
/*
FlyweightCharacterAbstractBuilder is a class holding the properties which are shared by
many objects. So instead of keeping these properties in those objects we keep them externally, making
objects flyweight. See more details in the comments of main function.
*/
class FlyweightCharacterAbstractBuilder {
FlyweightCharacterAbstractBuilder() {}
~FlyweightCharacterAbstractBuilder() {}
public:
static std::vector<float> fontSizes; // lets imagine that sizes may be of floating point type
static std::vector<std::string> fontNames; // font name may be of variable length (lets take 6 bytes is average)
static void setFontsAndNames();
static FlyweightCharacter createFlyweightCharacter(unsigned short fontSizeIndex,
unsigned short fontNameIndex,
unsigned short positionInStream);
};
std::vector<float> FlyweightCharacterAbstractBuilder::fontSizes(3);
std::vector<std::string> FlyweightCharacterAbstractBuilder::fontNames(3);
void FlyweightCharacterAbstractBuilder::setFontsAndNames() {
fontSizes[0] = 1.0;
fontSizes[1] = 1.5;
fontSizes[2] = 2.0;
fontNames[0] = "first_font";
fontNames[1] = "second_font";
fontNames[2] = "third_font";
}
class FlyweightCharacter {
unsigned short fontSizeIndex; // index instead of actual font size
unsigned short fontNameIndex; // index instead of font name
unsigned positionInStream;
public:
FlyweightCharacter(unsigned short fontSizeIndex, unsigned short fontNameIndex, unsigned short positionInStream):
fontSizeIndex(fontSizeIndex), fontNameIndex(fontNameIndex), positionInStream(positionInStream) {}
void print() {
std::cout << "Font Size: " << FlyweightCharacterAbstractBuilder::fontSizes[fontSizeIndex]
<< ", font Name: " << FlyweightCharacterAbstractBuilder::fontNames[fontNameIndex]
<< ", character stream position: " << positionInStream << std::endl;
}
~FlyweightCharacter() {}
};
FlyweightCharacter FlyweightCharacterAbstractBuilder::createFlyweightCharacter(unsigned short fontSizeIndex, unsigned short fontNameIndex, unsigned short positionInStream) {
FlyweightCharacter fc(fontSizeIndex, fontNameIndex, positionInStream);
return fc;
}
int main(int argc, char** argv) {
std::vector<FlyweightCharacter> chars;
FlyweightCharacterAbstractBuilder::setFontsAndNames();
unsigned short limit = NUMBER_OF_SAME_TYPE_CHARS;
for (unsigned short i = 0; i < limit; i++) {
chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(0, 0, i));
chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(1, 1, i + 1 * limit));
chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(2, 2, i + 2 * limit));
}
/*
Each char stores links to its fontName and fontSize so what we get is:
each object instead of allocating 6 bytes (convention above) for string
and 4 bytes for float allocates 2 bytes for fontNameIndex and fontSizeIndex.
That means for each char we save 6 + 4 - 2 - 2 = 6 bytes.
Now imagine we have NUMBER_OF_SAME_TYPE_CHARS = 1000 i.e. with our code
we will have 3 groups of chars with 1000 chars in each group which will save
3 * 1000 * 6 - (3 * 6 + 3 * 4) = 17970 saved bytes.
3 * 6 + 3 * 4 is a number of bytes allocated by FlyweightCharacterAbstractBuilder.
So the idea of the pattern is to move properties shared by many objects to some
external container. The objects in that case don't store the data themselves they
store only links to the data which saves memory and make the objects lighter.
The data size of properties stored externally may be significant which will save REALLY
huge amount of memory and will make each object super light in comparison to its counterpart.
That's where the name of the pattern comes from: flyweight (i.e. very light).
*/
for (unsigned short i = 0; i < chars.size(); i++) {
chars[i].print();
}
std::cin.get(); return 0;
}
代理模式為另一個物件提供一個替代者或佔位符,以控制對該物件的訪問。它用於用更簡單的物件表示一個複雜的物件。如果物件的建立成本很高,可以將其推遲到真正需要的時候,在此期間,一個更簡單的物件可以充當佔位符。這個佔位符物件被稱為複雜物件的“代理”。
#include <iostream>
#include <memory>
class ICar {
public:
virtual ~ICar() { std::cout << "ICar destructor!" << std::endl; }
virtual void DriveCar() = 0;
};
class Car : public ICar {
public:
void DriveCar() override { std::cout << "Car has been driven!" << std::endl; }
};
class ProxyCar : public ICar {
public:
ProxyCar(int driver_age) : driver_age_(driver_age) {}
void DriveCar() override {
if (driver_age_ > 16) {
real_car_->DriveCar();
} else {
std::cout << "Sorry, the driver is too young to drive." << std::endl;
}
}
private:
std::unique_ptr<ICar> real_car_ = std::make_unique<Car>();
int driver_age_;
};
int main() {
std::unique_ptr<ICar> car = std::make_unique<ProxyCar>(16);
car->DriveCar();
car = std::make_unique<ProxyCar>(25);
car->DriveCar();
return 0;
}
這種技術更廣泛地稱為 Mixin。混合類在文獻中被描述為表達抽象的強大工具[需要引用]。
基於介面的程式設計與模組化程式設計和麵向物件程式設計密切相關,它將應用程式定義為一組相互耦合的模組(相互連線,並透過介面相互連線)。模組可以被分離、替換或升級,而無需影響其他模組的內容。
整個系統的複雜性大大降低。基於介面的程式設計在模組化程式設計的基礎上更進一步,它要求在這些模組中新增介面。因此,整個系統被視為元件,而介面則幫助它們協同工作。
基於介面的程式設計提高了應用程式的模組化,從而提高了它在後期開發週期中的可維護性,尤其是在每個模組需要由不同的團隊開發的情況下。這是一種久負盛名的方法,它已經存在很長時間了,並且是 CORBA 等框架背後的核心技術[需要引用]。
當第三方為已建立的系統開發額外的元件時,這一點特別方便。他們只需要開發滿足父應用程式供應商指定的介面的元件即可。
因此,介面的釋出者保證他不會更改介面,而訂閱者同意完全實現介面,沒有任何偏差。因此,介面被認為是一種契約協議,基於此的程式設計正規化被稱為“基於介面的程式設計”。
責任鏈模式的目的是透過讓多個物件有機會處理請求來避免將請求的傳送者與接收者耦合。它將接收物件連結起來,並將請求沿著鏈傳遞,直到某個物件處理它。
#include <iostream>
using namespace std;
class Handler {
protected:
Handler *next;
public:
Handler() {
next = NULL;
}
virtual ~Handler() { }
virtual void request(int value) = 0;
void setNextHandler(Handler *nextInLine) {
next = nextInLine;
}
};
class SpecialHandler : public Handler {
private:
int myLimit;
int myId;
public:
SpecialHandler(int limit, int id) {
myLimit = limit;
myId = id;
}
~SpecialHandler() { }
void request(int value) {
if(value < myLimit) {
cout << "Handler " << myId << " handled the request with a limit of " << myLimit << endl;
} else if(next != NULL) {
next->request(value);
} else {
cout << "Sorry, I am the last handler (" << myId << ") and I can't handle the request." << endl;
}
}
};
int main () {
Handler *h1 = new SpecialHandler(10, 1);
Handler *h2 = new SpecialHandler(20, 2);
Handler *h3 = new SpecialHandler(30, 3);
h1->setNextHandler(h2);
h2->setNextHandler(h3);
h1->request(18);
h1->request(40);
delete h1;
delete h2;
delete h3;
return 0;
}
命令模式是一種物件行為模式,它透過將請求封裝為一個物件來解耦傳送者和接收者,從而允許您用不同的請求引數化客戶端,對請求進行排隊或記錄,並支援可撤消的操作。它也可以被認為是回撥方法的面向物件等效方法。
回撥:它是一個在使用者操作後在稍後時間註冊的函式。
#include <iostream>
using namespace std;
/*the Command interface*/
class Command
{
public:
virtual void execute()=0;
};
/*Receiver class*/
class Light {
public:
Light() { }
void turnOn()
{
cout << "The light is on" << endl;
}
void turnOff()
{
cout << "The light is off" << endl;
}
};
/*the Command for turning on the light*/
class FlipUpCommand: public Command
{
public:
FlipUpCommand(Light& light):theLight(light)
{
}
virtual void execute()
{
theLight.turnOn();
}
private:
Light& theLight;
};
/*the Command for turning off the light*/
class FlipDownCommand: public Command
{
public:
FlipDownCommand(Light& light) :theLight(light)
{
}
virtual void execute()
{
theLight.turnOff();
}
private:
Light& theLight;
};
class Switch {
public:
Switch(Command& flipUpCmd, Command& flipDownCmd)
:flipUpCommand(flipUpCmd),flipDownCommand(flipDownCmd)
{
}
void flipUp()
{
flipUpCommand.execute();
}
void flipDown()
{
flipDownCommand.execute();
}
private:
Command& flipUpCommand;
Command& flipDownCommand;
};
/*The test class or client*/
int main()
{
Light lamp;
FlipUpCommand switchUp(lamp);
FlipDownCommand switchDown(lamp);
Switch s(switchUp, switchDown);
s.flipUp();
s.flipDown();
}
給定一種語言,定義其語法的表示形式,以及使用該表示形式來解釋該語言中的句子的直譯器。
#include <iostream>
#include <string>
#include <map>
#include <list>
namespace wikibooks_design_patterns
{
// based on the Java sample around here
typedef std::string String;
struct Expression;
typedef std::map<String,Expression*> Map;
typedef std::list<Expression*> Stack;
struct Expression {
virtual int interpret(Map variables) = 0;
virtual ~Expression() {}
};
class Number : public Expression {
private:
int number;
public:
Number(int number) { this->number = number; }
int interpret(Map variables) { return number; }
};
class Plus : public Expression {
Expression* leftOperand;
Expression* rightOperand;
public:
Plus(Expression* left, Expression* right) {
leftOperand = left;
rightOperand = right;
}
~Plus(){
delete leftOperand;
delete rightOperand;
}
int interpret(Map variables) {
return leftOperand->interpret(variables) + rightOperand->interpret(variables);
}
};
class Minus : public Expression {
Expression* leftOperand;
Expression* rightOperand;
public:
Minus(Expression* left, Expression* right) {
leftOperand = left;
rightOperand = right;
}
~Minus(){
delete leftOperand;
delete rightOperand;
}
int interpret(Map variables) {
return leftOperand->interpret(variables) - rightOperand->interpret(variables);
}
};
class Variable : public Expression {
String name;
public:
Variable(String name) { this->name = name; }
int interpret(Map variables) {
if(variables.end() == variables.find(name)) return 0;
return variables[name]->interpret(variables);
}
};
// While the interpreter pattern does not address parsing, a parser is provided for completeness.
class Evaluator : public Expression {
Expression* syntaxTree;
public:
Evaluator(String expression){
Stack expressionStack;
size_t last = 0;
for (size_t next = 0; String::npos != last; last = (String::npos == next) ? next : (1+next)) {
next = expression.find(' ', last);
String token( expression.substr(last, (String::npos == next) ? (expression.length()-last) : (next-last)));
if (token == "+") {
Expression* right = expressionStack.back(); expressionStack.pop_back();
Expression* left = expressionStack.back(); expressionStack.pop_back();
Expression* subExpression = new Plus(right, left);
expressionStack.push_back( subExpression );
}
else if (token == "-") {
// it's necessary remove first the right operand from the stack
Expression* right = expressionStack.back(); expressionStack.pop_back();
// ..and after the left one
Expression* left = expressionStack.back(); expressionStack.pop_back();
Expression* subExpression = new Minus(left, right);
expressionStack.push_back( subExpression );
}
else
expressionStack.push_back( new Variable(token) );
}
syntaxTree = expressionStack.back(); expressionStack.pop_back();
}
~Evaluator() {
delete syntaxTree;
}
int interpret(Map context) {
return syntaxTree->interpret(context);
}
};
}
void main()
{
using namespace wikibooks_design_patterns;
Evaluator sentence("w x z - +");
static
const int sequences[][3] = {
{5, 10, 42}, {1, 3, 2}, {7, 9, -5},
};
for (size_t i = 0; sizeof(sequences)/sizeof(sequences[0]) > i; ++i) {
Map variables;
variables["w"] = new Number(sequences[i][0]);
variables["x"] = new Number(sequences[i][1]);
variables["z"] = new Number(sequences[i][2]);
int result = sentence.interpret(variables);
for (Map::iterator it = variables.begin(); variables.end() != it; ++it) delete it->second;
std::cout<<"Interpreter result: "<<result<<std::endl;
}
}
“迭代器”設計模式在 STL 中被大量使用,用於遍歷各種容器。充分理解它將使開發人員能夠建立高度可重用且易於理解的[需要引用]資料容器。
迭代器的基本思想是它允許遍歷容器(就像指標在陣列中移動一樣)。但是,要訪問容器的下一個元素,您不需要知道容器是如何構建的。這就是迭代器的作用。透過簡單地使用迭代器提供的成員函式,您可以按照容器的預期順序從第一個元素移動到最後一個元素。
讓我們首先考慮一個傳統的單維陣列,用指標從頭到尾移動。此示例假設您瞭解指標運算。請注意,以下的“it”或“itr”是“迭代器”的簡寫。
const int ARRAY_LEN = 42;
int *myArray = new int[ARRAY_LEN];
// Set the iterator to point to the first memory location of the array
int *arrayItr = myArray;
// Move through each element of the array, setting it equal to its position in the array
for(int i = 0; i < ARRAY_LEN; ++i)
{
// set the value of the current location in the array
*arrayItr = i;
// by incrementing the pointer, we move it to the next position in the array.
// This is easy for a contiguous memory container, since pointer arithmetic
// handles the traversal.
++arrayItr;
}
// Do not be messy, clean up after yourself
delete[] myArray;
這段程式碼對於陣列來說執行速度很快,但我們如何遍歷連結串列呢,因為記憶體不是連續的?考慮以下基本連結串列的實現
class IteratorCannotMoveToNext{}; // Error class
class MyIntLList
{
public:
// The Node class represents a single element in the linked list.
// The node has a next node and a previous node, so that the user
// may move from one position to the next, or step back a single
// position. Notice that the traversal of a linked list is O(N),
// as is searching, since the list is not ordered.
class Node
{
public:
Node():mNextNode(0),mPrevNode(0),mValue(0){}
Node *mNextNode;
Node *mPrevNode;
int mValue;
};
MyIntLList():mSize(0)
{}
~MyIntLList()
{
while(!Empty())
pop_front();
} // See expansion for further implementation;
int Size() const {return mSize;}
// Add this value to the end of the list
void push_back(int value)
{
Node *newNode = new Node;
newNode->mValue = value;
newNode->mPrevNode = mTail;
mTail->mNextNode = newNode;
mTail = newNode;
++mSize;
}
// Remove the value from the beginning of the list
void pop_front()
{
if(Empty())
return;
Node *tmpnode = mHead;
mHead = mHead->mNextNode;
delete tmpnode;
--mSize;
}
bool Empty()
{return mSize == 0;}
// This is where the iterator definition will go,
// but lets finish the definition of the list, first
private:
Node *mHead;
Node *mTail;
int mSize;
};
這個連結串列的記憶體不是連續的,因此不能使用指標運算。我們不想將連結串列的內部細節暴露給其他開發人員,迫使他們學習它們,並且會阻止我們改變它們。
這就是迭代器發揮作用的地方。公共介面使得學習容器的使用變得更容易,並將遍歷邏輯隱藏起來,防止其他開發人員看到它。
讓我們檢查一下迭代器本身的程式碼。
/*
* The iterator class knows the internals of the linked list, so that it
* may move from one element to the next. In this implementation, I have
* chosen the classic traversal method of overloading the increment
* operators. More thorough implementations of a bi-directional linked
* list would include decrement operators so that the iterator may move
* in the opposite direction.
*/
class Iterator
{
public:
Iterator(Node *position):mCurrNode(position){}
// Prefix increment
const Iterator &operator++()
{
if(mCurrNode == 0 || mCurrNode->mNextNode == 0)
throw IteratorCannotMoveToNext();e
mCurrNode = mCurrNode->mNextNode;
return *this;
}
// Postfix increment
Iterator operator++(int)
{
Iterator tempItr = *this;
++(*this);
return tempItr;
}
// Dereferencing operator returns the current node, which should then
// be dereferenced for the int. TODO: Check syntax for overloading
// dereferencing operator
Node * operator*()
{return mCurrNode;}
// TODO: implement arrow operator and clean up example usage following
private:
Node *mCurrNode;
};
// The following two functions make it possible to create
// iterators for an instance of this class.
// First position for iterators should be the first element in the container.
Iterator Begin(){return Iterator(mHead);}
// Final position for iterators should be one past the last element in the container.
Iterator End(){return Iterator(0);}
有了這個實現,現在就可以在不知道容器大小或其資料組織方式的情況下,按順序遍歷每個元素,操作或僅僅訪問資料。這透過 MyIntLList 類中的訪問器 Begin() 和 End() 來完成。
// Create a list
MyIntLList myList;
// Add some items to the list
for(int i = 0; i < 10; ++i)
myList.push_back(i);
// Move through the list, adding 42 to each item.
for(MyIntLList::Iterator it = myList.Begin(); it != myList.End(); ++it)
(*it)->mValue += 42;
以下程式給出了帶有通用模板的迭代器設計模式的實現
/************************************************************************/
/* Iterator.h */
/************************************************************************/
#ifndef MY_ITERATOR_HEADER
#define MY_ITERATOR_HEADER
#include <iterator>
#include <vector>
#include <set>
//////////////////////////////////////////////////////////////////////////
template<class T, class U>
class Iterator
{
public:
typedef typename std::vector<T>::iterator iter_type;
Iterator(U *pData):m_pData(pData){
m_it = m_pData->m_data.begin();
}
void first()
{
m_it = m_pData->m_data.begin();
}
void next()
{
m_it++;
}
bool isDone()
{
return (m_it == m_pData->m_data.end());
}
iter_type current()
{
return m_it;
}
private:
U *m_pData;
iter_type m_it;
};
template<class T, class U, class A>
class setIterator
{
public:
typedef typename std::set<T,U>::iterator iter_type;
setIterator(A *pData):m_pData(pData)
{
m_it = m_pData->m_data.begin();
}
void first()
{
m_it = m_pData->m_data.begin();
}
void next()
{
m_it++;
}
bool isDone()
{
return (m_it == m_pData->m_data.end());
}
iter_type current()
{
return m_it;
}
private:
A *m_pData;
iter_type m_it;
};
#endif
/************************************************************************/
/* Aggregate.h */
/************************************************************************/
#ifndef MY_DATACOLLECTION_HEADER
#define MY_DATACOLLECTION_HEADER
#include "Iterator.h"
template <class T>
class aggregate
{
friend class Iterator<T, aggregate>;
public:
void add(T a)
{
m_data.push_back(a);
}
Iterator<T, aggregate> *create_iterator()
{
return new Iterator<T, aggregate>(this);
}
private:
std::vector<T> m_data;
};
template <class T, class U>
class aggregateSet
{
friend class setIterator<T, U, aggregateSet>;
public:
void add(T a)
{
m_data.insert(a);
}
setIterator<T, U, aggregateSet> *create_iterator()
{
return new setIterator<T,U,aggregateSet>(this);
}
void Print()
{
copy(m_data.begin(), m_data.end(), std::ostream_iterator<T>(std::cout, "\n"));
}
private:
std::set<T,U> m_data;
};
#endif
/************************************************************************/
/* Iterator Test.cpp */
/************************************************************************/
#include <iostream>
#include <string>
#include "Aggregate.h"
using namespace std;
class Money
{
public:
Money(int a = 0): m_data(a) {}
void SetMoney(int a)
{
m_data = a;
}
int GetMoney()
{
return m_data;
}
private:
int m_data;
};
class Name
{
public:
Name(string name): m_name(name) {}
const string &GetName() const
{
return m_name;
}
friend ostream &operator<<(ostream& out, Name name)
{
out << name.GetName();
return out;
}
private:
string m_name;
};
struct NameLess
{
bool operator()(const Name &lhs, const Name &rhs) const
{
return (lhs.GetName() < rhs.GetName());
}
};
int main()
{
//sample 1
cout << "________________Iterator with int______________________________________" << endl;
aggregate<int> agg;
for (int i = 0; i < 10; i++)
agg.add(i);
Iterator< int,aggregate<int> > *it = agg.create_iterator();
for(it->first(); !it->isDone(); it->next())
cout << *it->current() << endl;
//sample 2
aggregate<Money> agg2;
Money a(100), b(1000), c(10000);
agg2.add(a);
agg2.add(b);
agg2.add(c);
cout << "________________Iterator with Class Money______________________________" << endl;
Iterator<Money, aggregate<Money> > *it2 = agg2.create_iterator();
for (it2->first(); !it2->isDone(); it2->next())
cout << it2->current()->GetMoney() << endl;
//sample 3
cout << "________________Set Iterator with Class Name______________________________" << endl;
aggregateSet<Name, NameLess> aset;
aset.add(Name("Qmt"));
aset.add(Name("Bmt"));
aset.add(Name("Cmt"));
aset.add(Name("Amt"));
setIterator<Name, NameLess, aggregateSet<Name, NameLess> > *it3 = aset.create_iterator();
for (it3->first(); !it3->isDone(); it3->next())
cout << (*it3->current()) << endl;
}
控制檯輸出
________________Iterator with int______________________________________ 0 1 2 3 4 5 6 7 8 9 ________________Iterator with Class Money______________________________ 100 1000 10000 ________________Set Iterator with Class Name___________________________ Amt Bmt Cmt Qmt
定義一個封裝一組物件如何互動的物件。中介者模式透過阻止物件顯式引用彼此來促進鬆散耦合,並且允許您獨立地改變它們的互動。
#include <iostream>
#include <string>
#include <list>
class MediatorInterface;
class ColleagueInterface {
std::string name;
public:
ColleagueInterface (const std::string& newName) : name (newName) {}
std::string getName() const {return name;}
virtual void sendMessage (const MediatorInterface&, const std::string&) const = 0;
virtual void receiveMessage (const ColleagueInterface*, const std::string&) const = 0;
};
class Colleague : public ColleagueInterface {
public:
using ColleagueInterface::ColleagueInterface;
virtual void sendMessage (const MediatorInterface&, const std::string&) const override;
private:
virtual void receiveMessage (const ColleagueInterface*, const std::string&) const override;
};
class MediatorInterface {
private:
std::list<ColleagueInterface*> colleagueList;
public:
const std::list<ColleagueInterface*>& getColleagueList() const {return colleagueList;}
virtual void distributeMessage (const ColleagueInterface*, const std::string&) const = 0;
virtual void registerColleague (ColleagueInterface* colleague) {colleagueList.emplace_back (colleague);}
};
class Mediator : public MediatorInterface {
virtual void distributeMessage (const ColleagueInterface*, const std::string&) const override;
};
void Colleague::sendMessage (const MediatorInterface& mediator, const std::string& message) const {
mediator.distributeMessage (this, message);
}
void Colleague::receiveMessage (const ColleagueInterface* sender, const std::string& message) const {
std::cout << getName() << " received the message from " << sender->getName() << ": " << message << std::endl;
}
void Mediator::distributeMessage (const ColleagueInterface* sender, const std::string& message) const {
for (const ColleagueInterface* x : getColleagueList())
if (x != sender) // Do not send the message back to the sender
x->receiveMessage (sender, message);
}
int main() {
Colleague *bob = new Colleague ("Bob"), *sam = new Colleague ("Sam"), *frank = new Colleague ("Frank"), *tom = new Colleague ("Tom");
Colleague* staff[] = {bob, sam, frank, tom};
Mediator mediatorStaff, mediatorSamsBuddies;
for (Colleague* x : staff)
mediatorStaff.registerColleague(x);
bob->sendMessage (mediatorStaff, "I'm quitting this job!");
mediatorSamsBuddies.registerColleague (frank); mediatorSamsBuddies.registerColleague (tom); // Sam's buddies only
sam->sendMessage (mediatorSamsBuddies, "Hooray! He's gone! Let's go for a drink, guys!");
return 0;
}
備忘錄模式在不違反封裝的情況下捕獲並外部化物件的內部狀態,以便稍後可以將物件恢復到該狀態。雖然四人幫使用朋友作為實現此模式的一種方式,但它不是最佳設計[需要引用]。它也可以使用PIMPL(指向實現的指標或不透明指標)來實現。最佳用例是編輯器中的“撤銷-重做”。
發起者(要儲存的物件)建立自身快照作為備忘錄物件,並將該引用傳遞給管理者物件。管理者物件保留備忘錄,直到發起者希望恢復備忘錄物件中記錄的先前狀態。
有關此模式的傳統示例,請參閱memoize。
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
const std::string NAME = "Object";
template <typename T>
std::string toString (const T& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
class Memento;
class Object {
private:
int value;
std::string name;
double decimal; // and suppose there are loads of other data members
public:
Object (int newValue): value (newValue), name (NAME + toString (value)), decimal ((float)value / 100) {}
void doubleValue() {value = 2 * value; name = NAME + toString (value); decimal = (float)value / 100;}
void increaseByOne() {value++; name = NAME + toString (value); decimal = (float)value / 100;}
int getValue() const {return value;}
std::string getName() const {return name;}
double getDecimal() const {return decimal;}
Memento* createMemento() const;
void reinstateMemento (Memento* mem);
};
class Memento {
private:
Object object;
public:
Memento (const Object& obj): object (obj) {}
Object snapshot() const {return object;} // want a snapshot of Object itself because of its many data members
};
Memento* Object::createMemento() const {
return new Memento (*this);
}
void Object::reinstateMemento (Memento* mem) {
*this = mem->snapshot();
}
class Command {
private:
typedef void (Object::*Action)();
Object* receiver;
Action action;
static std::vector<Command*> commandList;
static std::vector<Memento*> mementoList;
static int numCommands;
static int maxCommands;
public:
Command (Object *newReceiver, Action newAction): receiver (newReceiver), action (newAction) {}
virtual void execute() {
if (mementoList.size() < numCommands + 1)
mementoList.resize (numCommands + 1);
mementoList[numCommands] = receiver->createMemento(); // saves the last value
if (commandList.size() < numCommands + 1)
commandList.resize (numCommands + 1);
commandList[numCommands] = this; // saves the last command
if (numCommands > maxCommands)
maxCommands = numCommands;
numCommands++;
(receiver->*action)();
}
static void undo() {
if (numCommands == 0)
{
std::cout << "There is nothing to undo at this point." << std::endl;
return;
}
commandList[numCommands - 1]->receiver->reinstateMemento (mementoList[numCommands - 1]);
numCommands--;
}
void static redo() {
if (numCommands > maxCommands)
{
std::cout << "There is nothing to redo at this point." << std::endl;
return ;
}
Command* commandRedo = commandList[numCommands];
(commandRedo->receiver->*(commandRedo->action))();
numCommands++;
}
};
std::vector<Command*> Command::commandList;
std::vector<Memento*> Command::mementoList;
int Command::numCommands = 0;
int Command::maxCommands = 0;
int main()
{
int i;
std::cout << "Please enter an integer: ";
std::cin >> i;
Object *object = new Object(i);
Command *commands[3];
commands[1] = new Command(object, &Object::doubleValue);
commands[2] = new Command(object, &Object::increaseByOne);
std::cout << "0.Exit, 1.Double, 2.Increase by one, 3.Undo, 4.Redo: ";
std::cin >> i;
while (i != 0)
{
if (i == 3)
Command::undo();
else if (i == 4)
Command::redo();
else if (i > 0 && i <= 2)
commands[i]->execute();
else
{
std::cout << "Enter a proper choice: ";
std::cin >> i;
continue;
}
std::cout << " " << object->getValue() << " " << object->getName() << " " << object->getDecimal() << std::endl;
std::cout << "0.Exit, 1.Double, 2.Increase by one, 3.Undo, 4.Redo: ";
std::cin >> i;
}
}
觀察者模式定義了物件之間一對多的依賴關係,以便當一個物件改變狀態時,所有依賴它的物件都會被自動通知並更新。
- 問題
- 在應用程式中的一個或多個地方,我們需要了解系統事件或應用程式狀態變化。我們希望有一種標準的方式來訂閱監聽系統事件,以及一種標準的方式來通知相關方。通知應該在相關方訂閱系統事件或應用程式狀態變化後自動完成。還應該有一種取消訂閱的方法。
- 力量
- 觀察者和被觀察者可能應該由物件表示。觀察者物件將由被觀察者物件通知。
- 解決方案
- 訂閱後,監聽物件將透過方法呼叫方式被通知。
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
// The Abstract Observer
class ObserverBoardInterface
{
public:
virtual void update(float a,float b,float c) = 0;
};
// Abstract Interface for Displays
class DisplayBoardInterface
{
public:
virtual void show() = 0;
};
// The Abstract Subject
class WeatherDataInterface
{
public:
virtual void registerOb(ObserverBoardInterface* ob) = 0;
virtual void removeOb(ObserverBoardInterface* ob) = 0;
virtual void notifyOb() = 0;
};
// The Concrete Subject
class ParaWeatherData: public WeatherDataInterface
{
public:
void SensorDataChange(float a,float b,float c)
{
m_humidity = a;
m_temperature = b;
m_pressure = c;
notifyOb();
}
void registerOb(ObserverBoardInterface* ob)
{
m_obs.push_back(ob);
}
void removeOb(ObserverBoardInterface* ob)
{
m_obs.remove(ob);
}
protected:
void notifyOb()
{
list<ObserverBoardInterface*>::iterator pos = m_obs.begin();
while (pos != m_obs.end())
{
((ObserverBoardInterface* )(*pos))->update(m_humidity,m_temperature,m_pressure);
(dynamic_cast<DisplayBoardInterface*>(*pos))->show();
++pos;
}
}
private:
float m_humidity;
float m_temperature;
float m_pressure;
list<ObserverBoardInterface* > m_obs;
};
// A Concrete Observer
class CurrentConditionBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
CurrentConditionBoard(ParaWeatherData& a):m_data(a)
{
m_data.registerOb(this);
}
void show()
{
cout<<"_____CurrentConditionBoard_____"<<endl;
cout<<"humidity: "<<m_h<<endl;
cout<<"temperature: "<<m_t<<endl;
cout<<"pressure: "<<m_p<<endl;
cout<<"_______________________________"<<endl;
}
void update(float h, float t, float p)
{
m_h = h;
m_t = t;
m_p = p;
}
private:
float m_h;
float m_t;
float m_p;
ParaWeatherData& m_data;
};
// A Concrete Observer
class StatisticBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
StatisticBoard(ParaWeatherData& a):m_maxt(-1000),m_mint(1000),m_avet(0),m_count(0),m_data(a)
{
m_data.registerOb(this);
}
void show()
{
cout<<"________StatisticBoard_________"<<endl;
cout<<"lowest temperature: "<<m_mint<<endl;
cout<<"highest temperature: "<<m_maxt<<endl;
cout<<"average temperature: "<<m_avet<<endl;
cout<<"_______________________________"<<endl;
}
void update(float h, float t, float p)
{
++m_count;
if (t>m_maxt)
{
m_maxt = t;
}
if (t<m_mint)
{
m_mint = t;
}
m_avet = (m_avet * (m_count-1) + t)/m_count;
}
private:
float m_maxt;
float m_mint;
float m_avet;
int m_count;
ParaWeatherData& m_data;
};
int main(int argc, char *argv[])
{
ParaWeatherData * wdata = new ParaWeatherData;
CurrentConditionBoard* currentB = new CurrentConditionBoard(*wdata);
StatisticBoard* statisticB = new StatisticBoard(*wdata);
wdata->SensorDataChange(10.2, 28.2, 1001);
wdata->SensorDataChange(12, 30.12, 1003);
wdata->SensorDataChange(10.2, 26, 806);
wdata->SensorDataChange(10.3, 35.9, 900);
wdata->removeOb(currentB);
wdata->SensorDataChange(100, 40, 1900);
delete statisticB;
delete currentB;
delete wdata;
return 0;
}
狀態模式允許物件在內部狀態改變時改變其行為。該物件將看起來像改變了其類。
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <memory>
enum Input {DUCK_DOWN, STAND_UP, JUMP, DIVE};
class Fighter;
class StandingState; class JumpingState; class DivingState;
class FighterState {
public:
static std::shared_ptr<StandingState> standing;
static std::shared_ptr<DivingState> diving;
virtual ~FighterState() = default;
virtual void handleInput (Fighter&, Input) = 0;
virtual void update (Fighter&) = 0;
};
class DuckingState : public FighterState {
private:
int chargingTime;
static const int FullRestTime = 5;
public:
DuckingState() : chargingTime(0) {}
virtual void handleInput (Fighter&, Input) override;
virtual void update (Fighter&) override;
};
class StandingState : public FighterState {
public:
virtual void handleInput (Fighter&, Input) override;
virtual void update (Fighter&) override;
};
class JumpingState : public FighterState {
private:
int jumpingHeight;
public:
JumpingState() {jumpingHeight = std::rand() % 5 + 1;}
virtual void handleInput (Fighter&, Input) override;
virtual void update (Fighter&) override;
};
class DivingState : public FighterState {
public:
virtual void handleInput (Fighter&, Input) override;
virtual void update (Fighter&) override;
};
std::shared_ptr<StandingState> FighterState::standing (new StandingState);
std::shared_ptr<DivingState> FighterState::diving (new DivingState);
class Fighter {
private:
std::string name;
std::shared_ptr<FighterState> state;
int fatigueLevel = std::rand() % 10;
public:
Fighter (const std::string& newName) : name (newName), state (FighterState::standing) {}
std::string getName() const {return name;}
int getFatigueLevel() const {return fatigueLevel;}
virtual void handleInput (Input input) {state->handleInput (*this, input);} // delegate input handling to 'state'.
void changeState (std::shared_ptr<FighterState> newState) {state = newState; updateWithNewState();}
void standsUp() {std::cout << getName() << " stands up." << std::endl;}
void ducksDown() {std::cout << getName() << " ducks down." << std::endl;}
void jumps() {std::cout << getName() << " jumps into the air." << std::endl;}
void dives() {std::cout << getName() << " makes a dive attack in the middle of the jump!" << std::endl;}
void feelsStrong() {std::cout << getName() << " feels strong!" << std::endl;}
void changeFatigueLevelBy (int change) {fatigueLevel += change; std::cout << "fatigueLevel = " << fatigueLevel << std::endl;}
private:
virtual void updateWithNewState() {state->update(*this);} // delegate updating to 'state'
};
void StandingState::handleInput (Fighter& fighter, Input input) {
switch (input) {
case STAND_UP: std::cout << fighter.getName() << " remains standing." << std::endl; return;
case DUCK_DOWN: fighter.changeState (std::shared_ptr<DuckingState> (new DuckingState)); return fighter.ducksDown();
case JUMP: fighter.jumps(); return fighter.changeState (std::shared_ptr<JumpingState> (new JumpingState));
default: std::cout << "One cannot do that while standing. " << fighter.getName() << " remains standing by default." << std::endl;
}
}
void StandingState::update (Fighter& fighter) {
if (fighter.getFatigueLevel() > 0)
fighter.changeFatigueLevelBy(-1);
}
void DuckingState::handleInput (Fighter& fighter, Input input) {
switch (input) {
case STAND_UP: fighter.changeState (FighterState::standing); return fighter.standsUp();
case DUCK_DOWN:
std::cout << fighter.getName() << " remains in ducking position, ";
if (chargingTime < FullRestTime) std::cout << "recovering in the meantime." << std::endl;
else std::cout << "fully recovered." << std::endl;
return update (fighter);
default:
std::cout << "One cannot do that while ducking. " << fighter.getName() << " remains in ducking position by default." << std::endl;
update (fighter);
}
}
void DuckingState::update (Fighter& fighter) {
chargingTime++;
std::cout << "Charging time = " << chargingTime << "." << std::endl;
if (fighter.getFatigueLevel() > 0)
fighter.changeFatigueLevelBy(-1);
if (chargingTime >= FullRestTime && fighter.getFatigueLevel() <= 3)
fighter.feelsStrong();
}
void JumpingState::handleInput (Fighter& fighter, Input input) {
switch (input) {
case DIVE: fighter.changeState (FighterState::diving); return fighter.dives();
default:
std::cout << "One cannot do that in the middle of a jump. " << fighter.getName() << " lands from his jump and is now standing again." << std::endl;
fighter.changeState (FighterState::standing);
}
}
void JumpingState::update (Fighter& fighter) {
std::cout << fighter.getName() << " has jumped " << jumpingHeight << " feet into the air." << std::endl;
if (jumpingHeight >= 3)
fighter.changeFatigueLevelBy(1);
}
void DivingState::handleInput (Fighter& fighter, Input) {
std::cout << "Regardless of what the user input is, " << fighter.getName() << " lands from his dive and is now standing again." << std::endl;
fighter.changeState (FighterState::standing);
}
void DivingState::update (Fighter& fighter) {
fighter.changeFatigueLevelBy(2);
}
int main() {
std::srand(std::time(nullptr));
Fighter rex ("Rex the Fighter"), borg ("Borg the Fighter");
std::cout << rex.getName() << " and " << borg.getName() << " are currently standing." << std::endl;
int choice;
auto chooseAction = [&choice](Fighter& fighter) {
std::cout << std::endl << DUCK_DOWN + 1 << ") Duck down " << STAND_UP + 1 << ") Stand up " << JUMP + 1
<< ") Jump " << DIVE + 1 << ") Dive in the middle of a jump" << std::endl;
std::cout << "Choice for " << fighter.getName() << "? ";
std::cin >> choice;
const Input input1 = static_cast<Input>(choice - 1);
fighter.handleInput (input1);
};
while (true) {
chooseAction (rex);
chooseAction (borg);
}
}
定義一系列演算法,封裝每個演算法,並使它們可互換。策略模式允許演算法獨立於使用它的客戶端而改變。
#include <iostream>
using namespace std;
class StrategyInterface
{
public:
virtual void execute() const = 0;
};
class ConcreteStrategyA: public StrategyInterface
{
public:
void execute() const override
{
cout << "Called ConcreteStrategyA execute method" << endl;
}
};
class ConcreteStrategyB: public StrategyInterface
{
public:
void execute() const override
{
cout << "Called ConcreteStrategyB execute method" << endl;
}
};
class ConcreteStrategyC: public StrategyInterface
{
public:
void execute() const override
{
cout << "Called ConcreteStrategyC execute method" << endl;
}
};
class Context
{
private:
StrategyInterface * strategy_;
public:
explicit Context(StrategyInterface *strategy):strategy_(strategy)
{
}
void set_strategy(StrategyInterface *strategy)
{
strategy_ = strategy;
}
void execute() const
{
strategy_->execute();
}
};
int main(int argc, char *argv[])
{
ConcreteStrategyA concreteStrategyA;
ConcreteStrategyB concreteStrategyB;
ConcreteStrategyC concreteStrategyC;
Context contextA(&concreteStrategyA);
Context contextB(&concreteStrategyB);
Context contextC(&concreteStrategyC);
contextA.execute(); // output: "Called ConcreteStrategyA execute method"
contextB.execute(); // output: "Called ConcreteStrategyB execute method"
contextC.execute(); // output: "Called ConcreteStrategyC execute method"
contextA.set_strategy(&concreteStrategyB);
contextA.execute(); // output: "Called ConcreteStrategyB execute method"
contextA.set_strategy(&concreteStrategyC);
contextA.execute(); // output: "Called ConcreteStrategyC execute method"
return 0;
}
透過在一個操作中定義演算法的骨架,並將某些步驟推遲到子類中,模板方法模式允許子類重新定義該演算法的某些步驟,而不會改變演算法的結構。
#include <ctime>
#include <assert.h>
#include <iostream>
namespace wikibooks_design_patterns
{
/**
* An abstract class that is common to several games in
* which players play against the others, but only one is
* playing at a given time.
*/
class Game
{
public:
Game(): playersCount(0), movesCount(0), playerWon(-1)
{
srand( (unsigned)time( NULL));
}
/* A template method : */
void playOneGame(const int playersCount = 0)
{
if (playersCount)
{
this->playersCount = playersCount;
}
InitializeGame();
assert(this->playersCount);
int j = 0;
while (!endOfGame())
{
makePlay(j);
j = (j + 1) % this->playersCount;
if (!j)
{
++movesCount;
}
}
printWinner();
}
protected:
virtual void initializeGame() = 0;
virtual void makePlay(int player) = 0;
virtual bool endOfGame() = 0;
virtual void printWinner() = 0;
private:
void InitializeGame()
{
movesCount = 0;
playerWon = -1;
initializeGame();
}
protected:
int playersCount;
int movesCount;
int playerWon;
};
//Now we can extend this class in order
//to implement actual games:
class Monopoly: public Game {
/* Implementation of necessary concrete methods */
void initializeGame() {
// Initialize players
playersCount = rand() * 7 / RAND_MAX + 2;
// Initialize money
}
void makePlay(int player) {
// Process one turn of player
// Decide winner
if (movesCount < 20)
return;
const int chances = (movesCount > 199) ? 199 : movesCount;
const int random = MOVES_WIN_CORRECTION * rand() * 200 / RAND_MAX;
if (random < chances)
playerWon = player;
}
bool endOfGame() {
// Return true if game is over
// according to Monopoly rules
return (-1 != playerWon);
}
void printWinner() {
assert(playerWon >= 0);
assert(playerWon < playersCount);
// Display who won
std::cout<<"Monopoly, player "<<playerWon<<" won in "<<movesCount<<" moves."<<std::endl;
}
private:
enum
{
MOVES_WIN_CORRECTION = 20,
};
};
class Chess: public Game {
/* Implementation of necessary concrete methods */
void initializeGame() {
// Initialize players
playersCount = 2;
// Put the pieces on the board
}
void makePlay(int player) {
assert(player < playersCount);
// Process a turn for the player
// decide winner
if (movesCount < 2)
return;
const int chances = (movesCount > 99) ? 99 : movesCount;
const int random = MOVES_WIN_CORRECTION * rand() * 100 / RAND_MAX;
//std::cout<<random<<" : "<<chances<<std::endl;
if (random < chances)
playerWon = player;
}
bool endOfGame() {
// Return true if in Checkmate or
// Stalemate has been reached
return (-1 != playerWon);
}
void printWinner() {
assert(playerWon >= 0);
assert(playerWon < playersCount);
// Display the winning player
std::cout<<"Player "<<playerWon<<" won in "<<movesCount<<" moves."<<std::endl;
}
private:
enum
{
MOVES_WIN_CORRECTION = 7,
};
};
}
int main()
{
using namespace wikibooks_design_patterns;
Game* game = NULL;
Chess chess;
game = &chess;
for (unsigned i = 0; i < 100; ++i)
game->playOneGame();
Monopoly monopoly;
game = &monopoly;
for (unsigned i = 0; i < 100; ++i)
game->playOneGame();
return 0;
}
訪問者模式將表示對物件結構的元素執行的操作,它允許您定義新的操作,而無需更改其操作元素的類。
#include <string>
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Wheel;
class Engine;
class Body;
class Car;
// interface to all car 'parts'
struct CarElementVisitor
{
virtual void visit(Wheel& wheel) const = 0;
virtual void visit(Engine& engine) const = 0;
virtual void visit(Body& body) const = 0;
virtual void visitCar(Car& car) const = 0;
};
// interface to one part
struct CarElement
{
virtual void accept(const CarElementVisitor& visitor) = 0;
};
// wheel element, there are four wheels with unique names
class Wheel : public CarElement
{
public:
explicit Wheel(const string& name) : name_(name){}
const string& getName() const
{
return name_;
}
void accept(const CarElementVisitor& visitor)
{
visitor.visit(*this);
}
private:
string name_;
};
class Engine : public CarElement
{
public:
void accept(const CarElementVisitor& visitor)
{
visitor.visit(*this);
}
};
class Body : public CarElement
{
public:
void accept(const CarElementVisitor& visitor)
{
visitor.visit(*this);
}
};
class Car
{
public:
vector<unique_ptr<CarElement>>& getElements()
{
return elements_;
}
Car() {
// assume that neither push_back nor Wheel(const string&) may throw
elements_.push_back( make_unique<Wheel>("front left") );
elements_.push_back( make_unique<Wheel>("front right") );
elements_.push_back( make_unique<Wheel>("back left") );
elements_.push_back( make_unique<Wheel>("back right") );
elements_.push_back( make_unique<Body>() );
elements_.push_back( make_unique<Engine>() );
}
private:
vector<unique_ptr<CarElement>> elements_;
};
// PrintVisitor and DoVisitor show by using a different implementation the Car class is unchanged even though the algorithm is different in PrintVisitor and DoVisitor.
class CarElementPrintVisitor : public CarElementVisitor
{
public:
void visit(Wheel& wheel) const
{
cout << "Visiting " << wheel.getName() << " wheel" << endl;
}
void visit(Engine& engine) const
{
cout << "Visiting engine" << endl;
}
void visit(Body& body) const
{
cout << "Visiting body" << endl;
}
void visitCar(Car& car) const
{
cout << endl << "Visiting car" << endl;
vector<unique_ptr<CarElement>>& elems = car.getElements();
for(auto &it : elems)
{
// this issues the callback i.e. to this from the element
it->accept(*this);
}
cout << "Visited car" << endl;
}
};
class CarElementDoVisitor : public CarElementVisitor
{
public:
// these are specific implementations added to the original object without modifying the original struct
void visit(Wheel& wheel) const
{
cout << "Kicking my " << wheel.getName() << " wheel" << endl;
}
void visit(Engine& engine) const
{
cout << "Starting my engine" << endl;
}
void visit(Body& body) const
{
cout << "Moving my body" << endl;
}
void visitCar(Car& car) const
{
cout << endl << "Starting my car" << endl;
vector<unique_ptr<CarElement>>& elems = car.getElements();
for(auto& it : elems)
{
it->accept(*this); // this issues the callback i.e. to this from the element
}
cout << "Stopped car" << endl;
}
};
int main()
{
Car car;
CarElementPrintVisitor printVisitor;
CarElementDoVisitor doVisitor;
printVisitor.visitCar(car);
doVisitor.visitCar(car);
return 0;
}
一種經常被需要維護相同資料多個檢視的應用程式使用的模式。模型-檢視-控制器模式直到最近[需要引用]一直是一種非常常見的模式,尤其是對於圖形使用者介面程式設計,它將程式碼分成 3 部分:模型、檢視和控制器。
模型是實際的資料表示(例如,陣列 vs 連結串列)或其他表示資料庫的物件。檢視是讀取模型的介面或胖客戶端 GUI。控制器提供更改或修改資料的介面,然後選擇“最佳下一檢視”(NBV)。
新手可能會認為這種“MVC”模型很浪費,主要是因為您在執行時使用了很多額外的物件,而看起來一個巨大的物件就足夠了。但 MVC 模式成功的關鍵不在於編寫程式碼,而在於維護它,並允許人們修改程式碼而無需改變其他太多東西。此外,請記住,不同的開發人員有不同的優勢和劣勢,因此圍繞 MVC 進行團隊構建更容易。想象一個檢視團隊負責製作優秀的檢視,一個模型團隊精通資料,一個控制器團隊瞭解應用程式流程的全域性情況,處理請求,與模型協作,併為該客戶端選擇最合適的下一檢視。
例如:一個簡單的中央資料庫可以使用僅“模型”進行組織,例如,一個簡單的陣列。但是,在以後,使用連結串列可能更適用。所有陣列訪問都必須重新制作成各自的連結串列形式(例如,您將更改 myarray[5] 為 mylist.at(5) 或您使用的語言中任何等效的東西)。
好吧,如果我們遵循 MVC 模式,中央資料庫將使用某種函式進行訪問,例如 myarray.at(5)。如果我們將模型從陣列更改為連結串列,我們只需要用模型更改檢視,整個程式就會改變。保持介面不變,但更改其底層結構。這將允許我們比以前更自由、更快速地進行最佳化。
模型-檢視-控制器模式的一個巨大優勢顯然是在實現不同的檢視時能夠重用應用程式的邏輯(在模型中實現)。一個很好的例子是在 Web 開發中,一個常見的任務是在現有軟體中實現一個外部 API。如果 MVC 模式被幹淨地遵循,這隻需要修改控制器,控制器可以根據使用者代理請求的內容型別呈現不同型別的檢視。
