跳转到内容

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 模式已遵循干净的模式,则这只需要修改控制器,控制器能够根据用户代理请求的内容类型呈现不同类型的视图。

华夏公益教科书