跳轉到內容

C++ 程式設計/程式碼/設計模式/行為模式

來自華夏公益教科書,開放的書籍,開放的世界

行為模式

[編輯 | 編輯原始碼]

責任鏈

[編輯 | 編輯原始碼]

責任鏈模式的意圖是透過讓多個物件有機會處理請求,避免將請求的傳送者與接收者耦合。將接收物件連結起來,並將請求沿著鏈傳遞,直到某個物件處理它。

#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();
}

直譯器

[編輯 | 編輯原始碼]

給定一種語言,定義其語法的表示以及使用該表示來解釋該語言中的語句的直譯器。


Clipboard

待辦事項
最好讓讀者參考lexyacc,以及/或其衍生產品,如 flex 和 bison,以瞭解解決這些問題的另一種(經時間考驗的?)方法。



#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;


Clipboard

待辦事項

  • 討論 STL 中的迭代器,以及迭代器在演算法庫中的作用。
  • 迭代器最佳實踐
  • 建立和使用時的警告
  • 何時使用 operator[] 更好,並簡化理解
  • 關於模板對生成程式碼大小的影響的注意事項(這可能是一篇很好的學生研究論文)


以下程式給出了使用泛型模板的迭代器設計模式的實現

/************************************************************************/
/* 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;
}

備忘錄

[編輯 | 編輯原始碼]

備忘錄模式不會違反封裝,它將捕獲和外部化物件的內部狀態,以便該物件可以稍後恢復到該狀態。雖然 四人幫 使用 friend 作為實現這種模式的一種方式,但它不是最佳設計[需要引用]。它也可以使用 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;
}

模型-檢視-控制器 (MVC)

[編輯 | 編輯原始碼]

一種模式,常用於需要維護相同資料的多個檢視的應用程式。模型-檢視-控制器模式直到最近[需要引用] 是一個非常常見的模式,尤其是在圖形使用者介面程式設計中,它將程式碼分成 3 部分。模型、檢視和控制器。

模型是實際的資料表示(例如,陣列與連結串列)或其他表示資料庫的物件。檢視是讀取模型的介面或胖客戶端 GUI。控制器提供了更改或修改資料的介面,然後選擇“最佳下一個檢視”(NBV)。

新手可能會認為這種“MVC”模型是浪費的,主要是因為您在執行時使用了許多額外的物件,而這看起來像是可以用一個巨大的物件來完成的。但 MVC 模式的秘密不在於編寫程式碼,而在於維護程式碼,並允許人們修改程式碼而無需更改太多其他內容。此外,請記住,不同的開發人員有不同的優勢和劣勢,因此圍繞 MVC 進行團隊構建更容易。想象一個檢視團隊負責創建出色的檢視,一個模型團隊非常瞭解資料,以及一個控制器團隊負責瞭解應用程式流程的全域性情況,處理請求,與模型協作,併為該客戶端選擇最合適的下一個檢視。


Clipboard

待辦事項
嗯,請有人提出一個比以下示例更好的示例... 我想不出任何示例

  • 也許是一個銀行程式,用於客戶訪問其帳戶:傳統的瀏覽器上的 Web UI 與移動應用程式?然後處理支票賬戶/儲蓄賬戶與貸款與信用額度賬戶?僅供參考


例如:一個簡單的中央資料庫可以使用僅“模型”來組織,例如一個簡單的陣列。但是,稍後,使用連結串列可能更適用。所有陣列訪問都需要重新制作為相應的連結串列形式(例如,您將更改 myarray[5] 為 mylist.at(5),或您使用的語言中的等效項)。

好吧,如果我們遵循 MVC 模式,中央資料庫將使用某種函式進行訪問,例如 myarray.at(5)。如果我們將模型從陣列更改為連結串列,我們所要做的就是用模型更改檢視,整個程式就改變了。保持介面相同,但更改其底層機制。這將允許我們比以前更自由、更快地進行最佳化。

模型-檢視-控制器模式的優勢之一顯然是,在實現不同檢視時能夠重用應用程式的邏輯(在模型中實現)。一個很好的例子是在 Web 開發中,一個常見的任務是在現有軟體中實現外部 API。如果 MVC 模式已遵循乾淨的模式,則這隻需要修改控制器,控制器能夠根據使用者代理請求的內容型別呈現不同型別的檢視。

華夏公益教科書