1. 程式人生 > >設計模式示例(C++實現)

設計模式示例(C++實現)

最近參加了軟考,在準備考試過程中發現其中給出設計類圖讓通過指定的設計模式進行設計的題目對於練習C++和軟體開發是一種不錯的的方式,因為學校中的C++課程只講C++語言,雖然也講了封裝、繼承、多型等特性,但是缺少相應的練習,使得沒有深入的認識和理解。同時類似於程式設計、軟體工程的課程設計,其中概要設計、詳細設計也都是文件寫的很好看,最後編碼從沒按照設計中的內容來寫。最終導致了學完C++,用的時候卻不知從何下手。所以這裡總結了全國計算機技術與軟體專業技術資格(水平)考試參考用書系列中的部分題目,希望可以達到以下兩個目的:

* 練習C++,掌握理解更多的特性,而不是隻停留在掌握語法的階段

* 提高軟體開發方面的能力,主要是熟悉、學習基本的設計模式,學會如何設計並按照設計實現相應的編碼

但需要特別指出的是設計模式還是推薦閱讀這方面的經典書籍:設計模式 可複用面向物件軟體的基礎。我只是總結軟考中的題目是因為題目可以讓我們快速的對問題有所瞭解,並且我暫時也只是想練習C++,但如果想要對問題有系統、詳細的瞭解還是需要讀書。

Command(命令)模式

例:某燈具廠商欲生產一個燈具遙控器,該遙控器具有7個可程式設計的插槽,每個插槽都有開關按鈕,對應著一個不同的燈。利用該遙控器能夠統一控制房間內該廠商所有品牌燈具的開關,現採用Command(命令)模式實現該遙控器的軟體部分。Command模式的類圖如圖所示。

commandpattern

/*************************************************
* Command Pattern
* Author : robocoder
* Date : 2016 - 11 - 13
* HomePage : https://robocoderhan.github.io/
* Email : 
[email protected]
*************************************************/
#include <iostream> #include <string> using namespace std; class Light{ private: string name; public: Light(string name){ this->name = name; } void on(){ cout << name + " on" << endl; } void
off(){ cout << name + " off" << endl; } }; class Command{ public: virtual void execute(){} }; class LightOnCommand :public Command{ private: Light* light; public: LightOnCommand(Light* light){ this->light = light; } void execute(){ light->on(); } }; class LightOffCommand :public Command{ private: Light* light; public: LightOffCommand(Light* light){ this->light = light; } void execute(){ light->off(); } }; class RemoteControl{ private: Command* onCommands[7]; Command* offCommands[7]; public: RemoteControl(){} void setCommand(int slot, Command* onCommand, Command* offCommand){ onCommands[slot] = onCommand; offCommands[slot] = offCommand; } void onButtonWasPushed(int slot){ onCommands[slot]->execute(); } void offButtonWasPushed(int slot){ offCommands[slot]->execute(); } }; int main() { RemoteControl* remoteControl = new RemoteControl(); Light* livingRoomLight = new Light("Living Room"); Light* kitchenLight = new Light("Kitchen"); LightOnCommand* livingRoomLightOn = new LightOnCommand(livingRoomLight); LightOffCommand* livingRoomLightOff = new LightOffCommand(livingRoomLight); LightOnCommand* kitchenLightOn = new LightOnCommand(kitchenLight); LightOffCommand* kitchenLightOff = new LightOffCommand(kitchenLight); remoteControl->setCommand(0, livingRoomLightOn, livingRoomLightOff); remoteControl->setCommand(1, kitchenLightOn, kitchenLightOff); remoteControl->onButtonWasPushed(0); remoteControl->offButtonWasPushed(0); remoteControl->onButtonWasPushed(1); remoteControl->offButtonWasPushed(1); return 0; }

Observer(觀察者)模式

例:某實驗室欲建立一個實驗室環境監測系統,以顯示實驗室的溫度、溼度以及潔淨度等環境資料。當獲取到最新的環境測量資料時,顯示的環境資料能夠更新。現在採用觀察者(Observer)模式來開發該系統,觀察者模式的類圖如圖所示。

observerpattern

/*************************************************
* Observer Pattern
* Author : robocoder
* Date : 2016 - 11 - 13
* HomePage : https://robocoderhan.github.io/
* Email : [email protected]
*************************************************/

#include <iostream>
#include <vector>

using namespace std;

class Observer{
public:
    virtual void update(float temp, float humidity, float cleanness) = 0;
};

class Subject{
public:
    virtual void registerObserver(Observer* o) = 0;
    virtual void removeObserver(Observer* o) = 0;
    virtual void notifyObserver() = 0;
};

class EnvironmentData : public Subject{
private:
    vector<Observer*> observers;
    float temperature, humidity, cleanness;
public:
    void registerObserver(Observer* o){ observers.push_back(o); }
    void removeObserver(Observer* o){}
    void notifyObserver(){
        for (vector<Observer*>::const_iterator it = observers.begin(); it != observers.end(); it++)
        {
            (*it)->update(temperature, humidity, cleanness);
        }
    }
    void measurementsChanged(){ notifyObserver(); }
    void setMeasurements(float temperature, float humidity, float cleanness){
        this->temperature = temperature;
        this->humidity = humidity;
        this->cleanness = cleanness;
        measurementsChanged();
    }
};

class CurrentConditionsDisplay : public Observer{
private:
    float temperature, humidity, cleanness;
    Subject* envData;
public:
    CurrentConditionsDisplay(Subject* envData){
        this->envData = envData;
        this->envData->registerObserver(this);
    }
    void update(float temperature, float humidity, float cleanness){
        this->temperature = temperature;
        this->humidity = humidity;
        this->cleanness = cleanness;
        display();
    }
    void display(){
        cout << temperature << " " << humidity << " " << cleanness << endl;
    }
};

int main(){
    EnvironmentData* envData = new EnvironmentData();
    CurrentConditionsDisplay* currentDisplay = new CurrentConditionsDisplay(envData);
    envData->setMeasurements(80, 65, 30.4f);
    return 0;
}

Bridge(橋接)模式

例:欲開發一個繪圖軟體,要求使用不同的繪圖程式繪製不同的圖形。以繪製直線和圓形為例,對應的繪圖程式如表所示。

^-^ DP1 DP2
繪製直線 draw_a_line(x1,y1,x2,y2) drawline(x1,y1,x2,y2)
繪製圓 draw_a_circle(x,y,r) drawcircle(x,y,r)

該繪製軟體的擴充套件性要求,將不斷擴充新的圖形和新的繪圖程式。為了避免出現類爆炸的情況,現採用橋接(Bridge)模式來實現上述要求,得到如圖所示的類圖。

bridgepattern

/*************************************************
* Bridge Pattern
* Author : robocoder
* Date : 2016 - 11 - 15
* HomePage : https://robocoderhan.github.io/
* Email : [email protected]
*************************************************/

#include <iostream>

using namespace std;

class DP1{
public:
    static void draw_a_line(double x1, double y1, double x2, double y2){ cout << "DP1 draw_a_line" << endl; }
    static void draw_a_circle(double x, double y, double r){ cout << "DP1 draw_a_circle" << endl; }
};

class DP2{
public:
    static void drawline(double x1, double y1, double x2, double y2){ cout << "DP2 drawline" << endl; }
    static void drawcircle(double x, double y, double r){ cout << "Dp2 drawcircle" << endl; }
};

class Drawing{
public:
    virtual void drawLine(double x1, double y1, double x2, double y2) = 0;
    virtual void drawCircle(double x, double y, double r) = 0;
};

class V1Drawing : public Drawing{
public:
    void drawLine(double x1, double y1, double x2, double y2){ DP1::draw_a_line(x1, y1, x2, y2); cout << x1 << " " << y1 << " " << x2 << " " << y2 << endl; }
    void drawCircle(double x, double y, double r){ DP1::draw_a_circle(x, y, r); cout << x << " " << y << " " << r << endl; }
};

class V2Drawing : public Drawing{
public:
    void drawLine(double x1, double y1, double x2, double y2){ DP2::drawline(x1, y1, x2, y2); cout << x1 << " " << y1 << " " << x2 << " " << y2 << endl; }
    void drawCircle(double x, double y, double r){ DP2::drawcircle(x, y, r); cout << x << " " << y << " " << r << endl; }
};

class Shape{
public:
    Shape(Drawing* dp){ _dp = dp; }
    void drawLine(double x1, double y1, double x2, double y2){ _dp->drawLine(x1, y1, x2, y2); }
    void drawCircle(double x, double y, double r){ _dp->drawCircle(x, y, r); }
private:
    Drawing* _dp;
};

class Rectangle : public Shape{
private:
    double _x1, _y1, _x2, _y2;
public:
    Rectangle(Drawing* dp, double x1, double y1, double x2, double y2) : Shape(dp){ _x1 = x1; _y1 = y1; _x2 = x2; _y2 = y2; }
    void draw(){ drawLine(_x1, _y1, _x2, _y2); }
};

class Circle : public Shape{
private:
    double _x, _y, _r;
public:
    Circle(Drawing* dp, double x, double y, double r) : Shape(dp){ _x = x; _y = y; _r = r; }
    void draw(){ drawCircle(_x, _y, _r); }
};

int main(){
    V1Drawing* dp1 = new V1Drawing();
    V2Drawing* dp2 = new V2Drawing();
    Rectangle* rectangle1 = new Rectangle(dp1, 0, 1, 1, 1);
    rectangle1->draw();
    Rectangle* rectangle2 = new Rectangle(dp2, 0, 1, 1, 1);
    rectangle2->draw();
    Circle* circle1 = new Circle(dp1, 0, 1, 1);
    circle1->draw();
    Circle* circle2 = new Circle(dp2, 0, 1, 1);
    circle2->draw();
    return 0;
}

Prototype(原型)模式

例:現要求實現一個能夠自動生成求職簡歷的程式,簡歷的基本內容包括求職者的姓名、性別、年齡及工作經歷。希望每份簡歷中的工作經歷有所不同,並儘量減少程式中的重複程式碼。
現採用原型模式(Prototype)來實現上述要求,得到如圖所示的類圖。

prototypepattern

/*************************************************
* Prototype Pattern
* Author : robocoder
* Date : 2016 - 11 - 15
* HomePage : https://robocoderhan.github.io/
* Email : [email protected]
*************************************************/
#include <iostream>
#include <string>

using namespace std;

class Cloneable{
public:
    virtual Cloneable* clone() = 0;
};

class WorkExperience : public Cloneable{
private:
    string workDate;
    string company;
public:
    Cloneable* clone(){
        WorkExperience* obj = new WorkExperience();
        obj->workDate = this->workDate;
        obj->company = this->company;
        return obj;
    }
    void setWorkDate(string wd){ 
        workDate = wd; 
    }
    void setCompany(string comp){ 
        company = comp; 
    }
    string toString(){
        return workDate + company;
    }
};

class Resume : public Cloneable{
private:
    string name, sex, age;
    WorkExperience* work;
    Resume(WorkExperience* work){
        this->work = (WorkExperience*)work->clone();
    }
public:
    Resume(string name){ 
        work = new WorkExperience(); 
        this->name = name; 
    }
    void setPersonalInfo(string sex, string age){ 
        this->sex = sex; 
        this->age = age; 
    }
    void setWorkExperience(string workDate, string company){ 
        work->setWorkDate(workDate); 
        work->setCompany(company); 
    }
    Cloneable* clone(){
        Resume* obj = new Resume(this->work);
        obj->name = this->name;
        obj->sex = this->sex;
        obj->age = this->age;
        return obj;
    }
    string toString(){
        return name + sex + age + work->toString();
    }
};

int main(){
    Resume* a = new Resume("張三");
    a->setPersonalInfo("男", "29");
    a->setWorkExperience("1998-2000", "A company");
    cout << a->toString() << endl;
    Resume* b = (Resume*)a->clone();
    b->setWorkExperience("2001-2006", "B company");
    cout << b->toString() << endl;
    cout << a->toString() << endl;
    return 0;
}

Abstract Factory(抽象工廠)模式

例:現欲開發一個軟體系統,要求能夠同時支援多種不同的資料庫,為此採用抽象工廠模式設計該系統。以SQL Server和Access兩種資料庫以及系統中的資料庫表Department為例,其類圖如圖所示。

abstractfactorypattern

/*************************************************
* Abstract Factory Pattern
* Author : robocoder
* Date : 2016 - 11 - 16
* HomePage : https://robocoderhan.github.io/
* Email : [email protected]
*************************************************/
#include <iostream>

using namespace std;

//資料庫表
class Department{};

class IDepartment{
public:
    virtual void insert(Department* department) = 0;
    virtual Department* getDepartment(int id) = 0;
};

class SqlserverDepartment :public IDepartment{
public:
    void insert(Department* department){
        cout << "Insert a record into Department in SQL Server!\n";
    }
    Department* getDepartment(int id){ return new Department(); }
};

class AccessDepartment :public IDepartment{
public:
    void insert(Department* department){
        cout << "Insert a record into Department in ACCESS!\n";
    }
    Department* getDepartment(int id){ return new Department(); }
};

class IFactory{
public:
    virtual IDepartment* creatDepartment() = 0;
};

class SqlserverFactory :public IFactory{
public:
    IDepartment* creatDepartment(){
        return new SqlserverDepartment();
    }
};

class AccessFactory :public IFactory{
public:
    IDepartment* creatDepartment(){
        return new AccessDepartment();
    }
};

int main(){
    SqlserverFactory* sqlFactory = new SqlserverFactory();
    SqlserverDepartment* sqlDepartment = (SqlserverDepartment*)sqlFactory->creatDepartment();
    Department* a = new Department();
    sqlDepartment->insert(a);

    AccessFactory* accessFactory = new AccessFactory();
    AccessDepartment* accessDepartment = (AccessDepartment*)accessFactory->creatDepartment();
    accessDepartment->insert(a);
    return 0;
}

Decorator(裝飾)模式

例:某咖啡店在賣咖啡時,可以根據顧客的要求在其中加入各種配料,咖啡店會根據所加入的配料來計算費用。咖啡店所供應的咖啡及配料的種類和價格如表所示。

咖啡 價格/杯(¥) 配料 價格/杯(¥)
蒸餾咖啡(Espresso) 25 摩卡(Mocha) 10
深度烘焙咖啡(DarkRoast) 20 奶泡(Whip) 8

現採用裝飾器(Decorator)模式來實現計算費用的功能,得到如圖所示的類圖。

decoratorpattern

/*************************************************
* Decorator Pattern
* Author : robocoder
* Date : 2016 - 11 - 16
* HomePage : https://robocoderhan.github.io/
* Email : [email protected]
*************************************************/
#include <iostream>
#include <string>

using namespace std;

const int ESPRESSO_PRICE = 25;
const int DARKROAST_PRICE = 20;
const int MOCHA_PRICE = 10;
const int WHIP_PRICE = 8;

//飲料
class Beverage{
protected:
    string description;
public:
    virtual string getDescription(){
        return description;
    }
    virtual int cost() = 0;
};

//配料
class CondimentDecorator :public Beverage{
protected:
    Beverage* beverage;
};

//蒸餾咖啡
class Espresso :public Beverage{
public:
    Espresso()
    {
        description = "Espresso";
    }
    int cost(){
        return ESPRESSO_PRICE;
    }
};

//深度烘焙咖啡
class DarkRoast :public Beverage{
public:
    DarkRoast()
    {
        description = "DarkRoast";
    }
    int cost(){
        return DARKROAST_PRICE;
    }
};

//摩卡
class Mocha :public CondimentDecorator{
public:
    Mocha(Beverage* beverage){
        this->beverage = beverage;
    }
    string getDescription(){
        return beverage->getDescription() + ",Mocha";
    }
    int cost(){
        return MOCHA_PRICE + beverage->cost();
    }
};

//奶泡
class Whip :public CondimentDecorator{
public:
    Whip(Beverage* beverage){
        this->beverage = beverage;
    }
    string getDescription(){
        return beverage->getDescription() + ",Whip";
    }
    int cost(){
        return WHIP_PRICE + beverage->cost();
    }
};

int main(){
    Beverage* beverage = new DarkRoast();
    beverage = new Mocha(beverage);
    beverage = new Whip(beverage);
    cout << beverage->getDescription() << "¥" << beverage->cost() << endl;
    return 0;
}

Composition(組合)模式

例:某飯店在不同的時段提供多種不同的餐飲,其選單的結構圖如圖所示。

composition

現在採用組合(Composition)模式來構造該飯店的選單,使得飯店可以方便地在其中增加新的餐飲形式,得到如圖所示的類圖。其中MenuComposition為抽象類,定義了新增(add)新選單和列印飯店所有選單資訊(print)的方法介面。類Menu表示飯店提供的每種餐飲形式的選單,如煎餅屋選單、咖啡屋選單。每種選單中都可以新增子選單,例如圖中的甜點選單。類MenuItem表示選單中的菜式。

compositionpattern

/*************************************************
* Composition Pattern
* Author : robocoder
* Date : 2016 - 11 - 16
* HomePage : https://robocoderhan.github.io/
* Email : [email protected]
*************************************************/
#include <iostream>
#include <list>
#include <string>

using namespace std;

class MenuComponent{
protected:
    string name;
public:
    MenuComponent(string name){
        this->name = name;
    }
    string getName(){
        return name;
    }
    virtual void add(MenuComponent* menuComponent) = 0; //新增新選單
    virtual void print() = 0; //列印選單資訊
};

class MenuItem :public MenuComponent{
private:
    double price;
public:
    MenuItem(string name, double price) :MenuComponent(name){ this->price = price; }
    double getPrice(){ return price; }
    void add(MenuComponent* menuComponent){ return ; }
    void print(){
        cout << " " << getName() << "," << getPrice() << endl;
    }
};

class Menu :public MenuComponent{
private:
    list<MenuComponent*> menuComponents;
public:
    Menu(string name) :MenuComponent(name){}
    void add(MenuComponent* menuComponent){
        menuComponents.push_back(menuComponent); 
    }
    void print(){
        cout << "\n" << getName() << "\n-----------------------------" << endl;
        std::list<MenuComponent*>::iterator iter;
        for (iter = menuComponents.begin(); iter != menuComponents.end();iter++)
        {
            (*iter)->print();
        }
    }
};

int main(){
    MenuComponent* allMenus = new Menu("ALL MENUS");
    MenuComponent* dinerMenu = new Menu("DINER MENU");
    MenuComponent* coffee = new MenuItem("Espresso", 20);
    allMenus->add(coffee);
    MenuComponent* dumpling = new MenuItem("Dumpling", 30);
    dinerMenu->add(dumpling);
    allMenus->add(dinerMenu);
    allMenus->print();
    return 0;
}

State(狀態)模式

例:某大型商場內安裝了多個簡易的紙巾售賣機,自動出售2元錢一包的紙巾,且每次僅售出一包紙巾。紙巾售賣機的狀態圖如圖所示。

state

採用狀態(State)模式來實現該紙巾售賣機,得到如圖所示的類圖。其中類State為抽象類,定義了投幣、退幣、出紙巾等方法介面。類SoldState、SoldOutState、NoQuarterState和HasQuarterState分別對應圖中紙巾售賣機的4種狀態:售出紙巾、紙巾售完、沒有投幣、有2元錢。

statepattern

/*************************************************
* State Pattern
* Author : robocoder
* Date : 2016 - 11 - 17
* HomePage : https://robocoderhan.github.io/
* Email : [email protected]
*************************************************/
#include <iostream>

using namespace std;

class TissueMachine;

class State{
public:
    virtual void insertQuarter() = 0;//投幣
    virtual void ejectQuarter() = 0;//退幣
    virtual void turnCrank() = 0;//按下“出紙巾”按鈕
    virtual void dispense() = 0;//出紙巾
};

//沒有投幣
class NoQuarterState :public State{
private:
    TissueMachine* tissueMachine;
public:
    NoQuarterState(TissueMachine* tissueMachine){ this->tissueMachine = tissueMachine; }
    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
};

//有2元錢(已投幣)
class HasQuarterState :public State{
private:
    TissueMachine* tissueMachine;
public:
    HasQuarterState(TissueMachine* tissueMachine){ this->tissueMachine = tissueMachine; }
    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
};

//售出紙巾
class SoldState :public State{
private:
    TissueMachine* tissueMachine;
public:
    SoldState(TissueMachine* tissueMachine){ this->tissueMachine = tissueMachine; }
    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
};

//紙巾售完
class SoldOutState :public State{
private:
    TissueMachine* tissueMachine;
public:
    SoldOutState(TissueMachine* tissueMachine){ this->tissueMachine = tissueMachine; }
    void insertQuarter();
    void ejectQuarter();
    void turnCrank();
    void dispense();
};

class TissueMachine{
private:
    State *soldOutState, *noQuarterState, *hasQuarterState, *soldState, *state;
    int count;
public:
    TissueMachine(int numbers){
        soldOutState = new SoldOutState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        this->count = numbers;
        if (count > 0)
        {
            this->state = noQuarterState;
        }
    }

    void insertQuarter(){ state->insertQuarter(); }
    void ejectQuarter(){ state->ejectQuarter(); }
    void turnCrank(){
        state->turnCrank();
        state->dispense();
    }
    void setState(State* state){ this->state = state; }
    State* getHasQuarterState(){ return hasQuarterState; }
    State* getNoQuarterState(){ return noQuarterState; }
    State* getSoldState(){ return soldState; }
    State* getSoldOutState(){ return soldOutState; }
    void setCount(int numbers){ this->count = numbers; }
    int getCount(){ return count; }
};

void SoldOutState::insertQuarter(){ cout << "機器無紙巾,已退回硬幣!" << endl; }
void SoldOutState::ejectQuarter(){ cout << "自動售貨機根本沒有硬幣!" << endl; }
void SoldOutState::turnCrank(){ cout << "機器無紙巾,請不要操作機器" << endl; }
void SoldOutState::dispense(){}

void NoQuarterState::insertQuarter(){
    tissueMachine->setState(tissueMachine->getHasQuarterState());
    cout << "已投幣!" << endl;
}
void NoQuarterState::ejectQuarter(){ cout << "自動售貨機根本沒有硬幣!" << endl; }
void NoQuarterState::turnCrank(){ cout << "請投幣" << endl; }
void NoQuarterState::dispense(){}

void HasQuarterState::insertQuarter(){ cout << "已投幣!請不要重複投幣!已退回重複投幣!" << endl; }
void HasQuarterState::ejectQuarter(){
    tissueMachine->setState(tissueMachine->getNoQuarterState());
    cout << "已取幣!" << endl;
}
void HasQuarterState::turnCrank(){
    tissueMachine->setState(tissueMachine->getSoldState());
    cout << "請等待自動售貨機出紙巾!" << endl;
}
void HasQuarterState::dispense(){}

void SoldState::insertQuarter(){ cout << "請等待自動售貨機出紙巾!請不要投幣!已退回投幣!" << endl; }
void SoldState::ejectQuarter(){
    tissueMachine->setState(tissueMachine->getNoQuarterState());
    cout << "請等待自動售貨機出紙巾!無法取回已消費的硬幣!" << endl;
}
void SoldState::turnCrank(){ cout << "請等待自動售貨機出紙巾!已響應你的操作!" << endl; }
void SoldState::dispense(){
    if (tissueMachine->getCount() > 0){
        tissueMachine->setState(tissueMachine->getNoQuarterState());
        tissueMachine->setCount(tissueMachine->getCount() - 1);
        cout << "你的紙巾,請拿好!" << endl;
    }
    else{
        tissueMachine->setState(tissueMachine->getSoldOutState());
        cout << "已退回你的硬幣!紙巾已賣光,等待進貨!" << endl;
    }
}

int main(){
    TissueMachine *tissueMachine = new TissueMachine(1);
    cout << "紙巾數:" << tissueMachine->getCount() << endl;
    tissueMachine->insertQuarter();
    tissueMachine->turnCrank();
    cout << "紙巾數:" << tissueMachine->getCount() << endl;
    tissueMachine->turnCrank();
    cout << "紙巾數:" << tissueMachine->getCount() << endl;
    tissueMachine->insertQuarter();
    tissueMachine->turnCrank();
    return 0;
}

Chain of Responsibility(責任鏈)模式

例:已知某企業的採購審批是分級進行的,即根據採購金額的不同由不同層次的主管人員來審批,主任可以審批5萬元以下(不包括5萬元)的採購單,副董事長可以審批5萬元至10萬元(不包括10萬元)的採購單,董事長可以審批10萬元至50萬元(不包括50萬元)的採購單,50萬元及以上的採購單就需要開會討論決定。

採用責任鏈設計模式(Chain of Responsibility)對上述過程進行設計後得到的類圖如圖所示。

chainofresponsibilitypattern

/*************************************************
* Chain of Responsibility Pattern
* Author : robocoder
* Date : 2016 - 11 - 17
* HomePage : https://robocoderhan.github.io/
* Email : [email protected]
*************************************************/
#include <iostream>
#include <string>

using namespace std;

//採購單
class PurchaseRequest{
public:
    double amount;//採購金額
    int number;//採購單編號
    string purpose;//採購目的
};

//審批者
class Approver{
private:
    Approver* successor;
public:
    Approver(){
        successor = NULL;
    }
    virtual void processRequest(PurchaseRequest aRequest){
        if (successor != NULL)
        {
            successor->processRequest(aRequest);
        }
    }
    void setSuccessor(Approver* aSuccessor){
        successor = aSuccessor;
    }
};

//例會
class Congress :public Approver{
public:
    void processRequest(PurchaseRequest aRequest){
        if (aRequest.amount >= 500000){
            cout << "例會正在審批" << endl;
        }
        else
        {
            Approver::processRequest(aRequest);
        }
    }
};

//董事長
class President :public Approver{
public:
    void processRequest(PurchaseRequest aRequest){
        if (aRequest.amount >= 100000 && aRequest.amount < 500000)
        {
            cout << "董事長正在審批" << endl;
        }
        else
        {
            Approver::processRequest(aRequest);
        }
    }
};

//副董事長
class VicePresident :public Approver{
public:
    void processRequest(PurchaseRequest aRequest){
        if (aRequest.amount >= 50000 && aRequest.amount < 100000)
        {
            cout << "副董事長正在審批" << endl;
        }
        else
        {
            Approver::processRequest(aRequest);
        }
    }
};

//主任
class Director :public Approver{
public:
    void processRequest(PurchaseRequest aRequest){
        if (aRequest.amount < 50000)
        {
            cout << "主任正在審批" << endl;
        } 
        else
        {
            Approver::processRequest(aRequest);
        }
    }
};

int main(){
    Congress meeting;
    President Tammy;
    VicePresident Sam;
    Director Larry;
    meeting.setSuccessor(NULL);
    Tammy.setSuccessor(&meeting);
    Sam.setSuccessor(&Tammy);
    Larry.setSuccessor(&Sam);

    PurchaseRequest aRequest;
    cin >> aRequest.amount;
    Larry.processRequest(aRequest);
    return 0;
}

Strategy(策略)模式

例:某遊戲公司現欲開發一款面向兒童的模擬遊戲,該遊戲主要模擬現實世界中各種鴨子的發聲特徵、飛行特徵和外觀特徵。遊戲需要模擬的鴨子種類及其特徵如表所示。

鴨子種類 發聲特徵 飛行特徵 外觀特徵
灰鴨(MallardDuck) 發出“嘎嘎”聲(Quack) 用翅膀飛行(FlyWithWings) 灰色羽毛
紅頭鴨(RedHeadDuck) 發出“嘎嘎”聲(Quack) 用翅膀飛行(FlyWithWings) 灰色羽毛、頭部紅色
棉花鴨(CottonDuck) 不發聲(QuackNoWay) 不能飛行(FlyNoWay) 白色
橡皮鴨(RubberDuck) 發出橡皮與空氣摩擦的聲音(Squeak) 不能飛行(FlyNoWay) 黑白橡皮色

為支援將來能夠模擬更多種類鴨子的特性,採用策略(Strategy)模式設計的類圖如圖所示。

strategy

其中,Duck為抽象類,描述了抽象的鴨子,而類RubberDuck、MallardDuck、CottonDuck和RedHeadDuck分別描述具體的鴨子種類,方法fly()、quack()和display()分別表示不同種類的鴨子都具有飛行特徵、發聲特徵和外觀特徵;類FlyBehavior與QuackBehavior為抽象類,分別用於表示抽象的飛行行為與發聲行為;類FlyNoWay與FlyWithWings分別描述不能飛行的行為和用翅膀飛行的行為;類Quack、Squeak與QuackNoWay分別描述發出“嘎嘎”聲的行為、發出橡皮與空氣摩擦聲的行為與不發聲的行為。

/*************************************************
* Strategy Pattern
* Author : robocoder
* Date : 2016 - 11 - 17
* HomePage : https://robocoderhan.github.io/
* Email : [email protected]
*************************************************/
#include <iostream>

using namespace std;

class FlyBehavior{
public:
    virtual void fly() = 0;
};

class QuackBehavior{
public:
    virtual void quack() = 0;
};

class FlyWithWings :public FlyBehavior{
public:
    void fly(){ cout << "使用翅膀飛行" << endl; }
};

class FlyNoWay :public FlyBehavior{
public:
    void fly(){ cout << "不能飛行" << endl; }
};

class Quack :public QuackBehavior{
public:
    void quack(){ cout << "發出\'嘎嘎\'聲!" << endl; }
};

class Squeak :public QuackBehavior{
public:
    void quack(){ cout << "發出空氣與橡皮摩擦聲!" << endl; }
};

class QuackNoWay :public QuackBehavior{
public:
    void quack(){ cout << "不能發聲!" << endl; }
};

class Duck{
protected:
    FlyBehavior* flyBehavior;
    QuackBehavior* quackBehavior;
public:
    void fly(){ flyBehavior->fly(); }
    void quack(){ quackBehavior->quack(); }
    virtual void display() = 0;
};

class RubberDuck :public Duck{
public:
    RubberDuck(){
        flyBehavior = new FlyNoWay();
        quackBehavior = new Squeak();
    }
    ~RubberDu