Overview of design patterns behavioral patterns

Posted by Ima2003 on Sat, 07 Mar 2020 11:48:34 +0100

The first two articles sorted out the creation mode and structure mode in the three categories of design mode respectively:

Design pattern Overview - creation pattern 

Design pattern Overview - structural pattern

Today, let's take a look at the last part, behavioral model.

First of all, what is behavioral model?

Behavioral pattern is a design pattern that abstracts responsibilities and algorithms among different objects. It not only focuses on the structure of classes and objects, but also focuses on the interaction between them.

For a system, objects do not run in isolation. Objects can communicate and cooperate with each other to complete some complex functions. Objects interact with each other.

Behavioral model can be divided into two types: quasi behavioral model and object behavioral model. Class behavior pattern uses inheritance relationship to distribute behavior among several classes, mainly through polymorphism and other ways to allocate responsibilities of parent and child classes. The object behavior pattern is to allocate responsibilities by aggregating and associating objects. Most behavioral design patterns belong to object behavioral design patterns.

Behavioral pattern includes 11 specific design patterns: chain of responsibility pattern, command pattern, Interpreter pattern, iterator pattern, mediator pattern, memo pattern, observer pattern, state pattern Pattern, strategy pattern, template method pattern, visitor pattern.

We choose five of them to introduce.

1. Command pattern

Command pattern is also called action pattern or transaction pattern. In this mode, a request is encapsulated into an object, so that different requests can be used to parameterize customers, queue or log requests, and support revocable operations.

The command mode can completely decouple the sender and the receiver. There is no direct reference relationship between the sender and the receiver. The object that sends the request only needs to know how to send the request without knowing how to complete the request.

The command mode consists of five roles:
(1) Abstract command class Command
(2) Concrete command class
(3) Caller Invoker
(4) Receiver
(5) Client

Example code:

#include <iostream>
#include "ConcreteCommand.h"
#include "Invoker.h"
#include "Receiver.h"

using namespace std;

int main(int argc, char *argv[])
{
	Receiver * pReceiver = new Receiver();   // Define a recipient
	ConcreteCommand * pCommand = new ConcreteCommand(pReceiver);  // Define a specific command
	Invoker * pInvoker = new Invoker(pCommand);    //Define the caller of the command
	pInvoker->call();
	
	delete pReceiver;
	delete pCommand;
	delete pInvoker;
	return 0;
}
///////////////////////////////////////////////////////////
//  Receiver.h
//  Definition of the Class Receiver
///////////////////////////////////////////////////////////

#ifndef __RECEIVER_H__
#define __RECEIVER_H__

class Receiver
{

public:
	Receiver();
	virtual ~Receiver();

	void action();

};

#endif
///////////////////////////////////////////////////////////
//  Receiver.cpp
//  Implementation of the Class Receiver
///////////////////////////////////////////////////////////

#include "Receiver.h"
#include <iostream>
using namespace std;

Receiver::Receiver()
{

}

Receiver::~Receiver()
{

}

void Receiver::action()
{
	cout << "Receiver action." << endl;
}
///////////////////////////////////////////////////////////
//  ConcreteCommand.h
//  Definition of the Class ConcreteCommand
///////////////////////////////////////////////////////////

#ifndef __CONCRETE_COMMAND_H__
#define __CONCRETE_COMMAND_H__

#include "Command.h"
#include "Receiver.h"

class ConcreteCommand : public Command
{

public:
	ConcreteCommand(Receiver * pReceiver);
	virtual ~ConcreteCommand();
	virtual void execute();

private:
	Receiver *m_pReceiver;

};

#endif 
///////////////////////////////////////////////////////////
//  ConcreteCommand.cpp
//  Implementation of the Class ConcreteCommand
///////////////////////////////////////////////////////////

#include "ConcreteCommand.h"
#include <iostream>
using namespace std;

ConcreteCommand::ConcreteCommand(Receiver *pReceiver)
{
	m_pReceiver = pReceiver;
}

ConcreteCommand::~ConcreteCommand()
{

}

void ConcreteCommand::execute()
{
	cout << "ConcreteCommand::execute"  << endl;
	m_pReceiver->action();
}
///////////////////////////////////////////////////////////
//  Invoker.h
//  Definition of the Class Invoker
///////////////////////////////////////////////////////////

#ifndef __INVOKER_H__
#define __INVOKER_H__

#include "Command.h"

class Invoker
{

public:
	Invoker(Command * pCommand);
	virtual ~Invoker();
	void call();

private:
	Command *m_pCommand;


};

#endif 
///////////////////////////////////////////////////////////
//  Invoker.cpp
//  Implementation of the Class Invoker
///////////////////////////////////////////////////////////

#include "Invoker.h"
#include <iostream>
using namespace std;

Invoker::Invoker(Command * pCommand)
{
	m_pCommand = pCommand;
}

Invoker::~Invoker()
{

}

void Invoker::call()
{
	cout << "Invoker calling" << endl;
	m_pCommand->execute();
}

In the command mode, the abstract command class declares the execute() and other methods used to execute the request, through which the relevant operations of the request receiver can be called; the concrete command class is a subclass of the abstract command class, which implements the methods declared in the abstract command class, binding the actions of the receiver object to the concrete receiver object; the caller is the sending of the request The requester, also known as requester, performs the request through the command object; the executor (receiver) performs the operation related to the request, which specifically implements the business processing of the request.

The essence of command mode is to encapsulate the command, separating the responsibility of issuing and executing the command. The command mode treats the request itself as an object, which can be stored and passed as other objects. This mode can reduce the coupling degree of the system, facilitate the addition of new commands, easily implement command queue and macro commands, and realize the cancellation and recovery of requests.

It's difficult to understand this pattern for the first time. You can understand it well by reading it twice more and thinking about it. Imagine a TV and a remote control. The TV is the receiver (and executor) of the command. The remote control is the sender of the command. The specific command can be power on, power off, program selection, etc. these commands can be abstracted into a unified command interface.

2. Mediator pattern

Mediator pattern is to use a mediation object to encapsulate different object interactions, so that the objects do not need to explicitly reference each other, so as to achieve the purpose of loose coupling.

The mediator model has four roles:
(1) Mediator, abstract mediator
(2) Concrete mediator
(3) Abstract Colleague class
(4) Concrete colleague

Example code:

#include <iostream>
#include "ConcreteColleagueA.h"
#include "ConcreteMediator.h"
#include "ConcreteColleagueB.h"

using namespace std;

int main(int argc, char *argv[])
{
	ConcreteColleagueA * pa = new ConcreteColleagueA();
	ConcreteColleagueB * pb = new ConcreteColleagueB();
	ConcreteMediator * pm = new ConcreteMediator();
	pm->registered(1,pa);
	pm->registered(2,pb);
	
	// sendmsg from a to b
	pa->sendmsg(2,"Hello, This is a.");
	// sendmsg from b to a
	pb->sendmsg(1,"Hello, This is b.");
	
	delete pa, pb, pm;
	
	return 0;
}
///////////////////////////////////////////////////////////
//  ConcreteMediator.h
//  Definition of the Class ConcreteMediator
///////////////////////////////////////////////////////////

#ifndef __CONCRETE_MEDIATOR_H__
#define __CONCRETE_MEDIATOR_H__

#include "ConcreteColleagueB.h"
#include "Mediator.h"
#include "ConcreteColleagueA.h"
#include <map>

using namespace std;

class ConcreteMediator : public Mediator
{

public:
	ConcreteMediator();
	virtual ~ConcreteMediator();

	virtual void operation(int nWho, string str);
	virtual void registered(int nWho, Colleague * aColleague);
	
private:
	map<int,Colleague*> m_mpColleague;
};

#endif 
///////////////////////////////////////////////////////////
//  ConcreteMediator.cpp
//  Implementation of the Class ConcreteMediator
///////////////////////////////////////////////////////////

#include "ConcreteMediator.h"
#include <map>
#include <iostream>
using namespace std;

ConcreteMediator::ConcreteMediator()
{

}

ConcreteMediator::~ConcreteMediator()
{

}

void ConcreteMediator::operation(int nWho, string str)
{
	map<int,Colleague*>::const_iterator itr = m_mpColleague.find(nWho);
	if(itr == m_mpColleague.end())
	{
		cout << "not found this colleague!" << endl;
		return;
	}
	
	Colleague* pc = itr->second;
	pc->receivemsg(str);
}


void ConcreteMediator::registered(int nWho, Colleague * aColleague)
{
	map<int,Colleague*>::const_iterator itr = m_mpColleague.find(nWho);
	if(itr == m_mpColleague.end())
	{
		//Insert new object
		m_mpColleague.insert(make_pair(nWho, aColleague));
		
		//Expose the mediation class to colleague at the same time 
		aColleague->setMediator(this);
	}
}
///////////////////////////////////////////////////////////
//  ConcreteColleagueA.h
//  Definition of the Class ConcreteColleagueA
///////////////////////////////////////////////////////////

#ifndef __CONCRETE_COLLEAGUE_A_H__
#define __CONCRETE_COLLEAGUE_A_H__

#include "Colleague.h"

class ConcreteColleagueA : public Colleague
{
public:
	ConcreteColleagueA();
	virtual ~ConcreteColleagueA();

	virtual void sendmsg(int toWho, string str);
	virtual void receivemsg(string str);

};

#endif 
///////////////////////////////////////////////////////////
//  ConcreteColleagueA.cpp
//  Implementation of the Class ConcreteColleagueA
///////////////////////////////////////////////////////////

#include "ConcreteColleagueA.h"
#include <iostream>

using namespace std;

ConcreteColleagueA::ConcreteColleagueA()
{
}

ConcreteColleagueA::~ConcreteColleagueA()
{
}

void ConcreteColleagueA::sendmsg(int toWho, string str)
{
	cout << "send msg from colleagueA,to:" << toWho << endl;
	
	m_pMediator->operation(toWho, str);
}

void ConcreteColleagueA::receivemsg(string str)
{
	cout << "ConcreteColleagueA reveivemsg:" << str <<endl;
}

In the mediator pattern, the abstract mediator is used to define an interface, which is used to communicate with each colleague object. The concrete mediator is a subclass of the abstract mediator, which implements cooperative behavior by coordinating each colleague object and maintaining its reference in each colleague object. The abstract colleague class defines the common methods of each colleague. The concrete colleague class is the abstract colleague class Each colleague object references a mediator object. When each colleague object needs to communicate with other colleague objects, it first communicates with the mediator, which indirectly completes the communication with other colleague objects. The methods defined in the abstract colleague class are implemented in the concrete colleague class.

Through the introduction of mediator object, the original network structure of peer-to-peer system becomes a star structure with mediator as the center, and the mediator plays a role of transition and coordination. As the core, mediators control and coordinate the whole system, so as to simplify the interaction between objects, and further control the interaction between objects, such as permission management between colleagues at different levels.

3. Observer pattern

Observer mode is used to define a one to many dependency relationship between objects, so that whenever the state of an object changes, its dependent objects will be notified and updated automatically. The observer pattern is also called publish / subscribe pattern, model / view pattern, source / listener pattern or dependencies pattern.

The observer pattern consists of four roles:
(1) Target Subject
(2) Concrete subject
(3) Observer
(4) Concrete observer

Example code:

#include <iostream>
#include "Subject.h"
#include "Observer.h"
#include "ConcreteObserver.h"
#include "ConcreteSubject.h"

using namespace std;

int main(int argc, char *argv[])
{
	Subject * subject = new ConcreteSubject();
	Observer * objA = new ConcreteObserver("A");
	Observer * objB = new ConcreteObserver("B");
	subject->attach(objA);
	subject->attach(objB);
	
	subject->setState(1);
	subject->notify();
	
	subject->detach(objB);
	subject->setState(2);
	subject->notify();
	
	delete subject;
	delete objA;
	delete objB;
		
	return 0;
}
///////////////////////////////////////////////////////////
//  Subject.h
//  Definition of the Class Subject
///////////////////////////////////////////////////////////

#ifndef __SUBJECT_H__
#define __SUBJECT_H__

#include "Observer.h"
#include <vector>
using namespace std;

class Subject
{

public:
	Subject();
	virtual ~Subject();
	Observer *m_Obeserver;

	void attach(Observer * pObserver);
	void detach(Observer * pObserver);
	void notify();
		
	virtual int getState() = 0;
	virtual void setState(int i)= 0;
	
private:
	vector<Observer*> m_vtObj;

};

#endif 
///////////////////////////////////////////////////////////
//  Subject.cpp
//  Implementation of the Class Subject
///////////////////////////////////////////////////////////

#include "Subject.h"

Subject::Subject()
{

}

Subject::~Subject()
{

}

void Subject::attach(Observer * pObserver)
{
	m_vtObj.push_back(pObserver);
}

void Subject::detach(Observer * pObserver)
{
	vector<Observer*>::iterator itr;
	for(itr = m_vtObj.begin(); itr != m_vtObj.end(); itr++)
	{
		if(*itr == pObserver)
		{
			m_vtObj.erase(itr);
			return;
		}			
	}
}

void Subject::notify(){
	for(vector<Observer*>::iterator itr = m_vtObj.begin();
		itr != m_vtObj.end();
	 	itr++)
	{	
		(*itr)->update(this);		
	}
}
///////////////////////////////////////////////////////////
//  Observer.h
//  Definition of the Class Observer
///////////////////////////////////////////////////////////

#ifndef __OBSERVER_H__
#define __OBSERVER_H__

class Subject;

class Observer
{

public:
	Observer();
	virtual ~Observer();
	virtual void update(Subject * sub) = 0;
};

#endif 
///////////////////////////////////////////////////////////
//  ConcreteObserver.h
//  Definition of the Class ConcreteObserver
///////////////////////////////////////////////////////////

#ifndef __CONCRETE_OBSERVER_H__
#define __CONCRETE_OBSERVER_H__

#include "Obeserver.h"
#include <string>

using namespace std;

class ConcreteObserver : public Obeserver
{

public:
	ConcreteObserver(string name);
	virtual ~ConcreteObserver();
	virtual void update(Subject * sub);

private:
	string m_objName;
	int m_obeserverState;
};

#endif 
///////////////////////////////////////////////////////////
//  ConcreteObserver.cpp
//  Implementation of the Class ConcreteObserver
///////////////////////////////////////////////////////////

#include "ConcreteObserver.h"
#include <iostream>
#include <vector>
#include "Subject.h"
using namespace std;

ConcreteObserver::ConcreteObserver(string name)
{
	m_objName = name;
}

ConcreteObserver::~ConcreteObserver()
{

}

void ConcreteObserver::update(Subject * sub)
{
	m_obeserverState = sub->getState();
	cout << "update oberserver[" << m_objName << "] state:" << m_obeserverState << endl;
}

Execution result:

Observer pattern defines a one to many dependency relationship between objects, so that whenever an object's state changes, its dependent objects will be notified and updated. Observer pattern can realize the separation of presentation layer and data logic layer, and establish an abstract coupling between observation target and observer, supporting broadcast communication.

4. State pattern

State mode allows an object to change its behavior when its internal state changes, and the object appears to modify its class.

The state mode consists of three roles:
(1) Environment class Context
(2) Abstract State class State
(3) Concrete state

Example code:

#include <iostream>
#include "Context.h"
#include "ConcreteStateA.h"
#include "ConcreteStateB.h"

using namespace std;

int main(int argc, char *argv[])
{
	Context * c = new Context();
	c->request();
	c->request();
	c->request();

	delete c;
	return 0;
}
///////////////////////////////////////////////////////////
//  Context.h
//  Definition of the Class Context
///////////////////////////////////////////////////////////

#ifndef __CONTEXT_H__
#define __CONTEXT_H__

#include "State.h"

class Context
{

public:
	Context();
	virtual ~Context();

	void changeState(State * st);
	void request();

private:
	State *m_pState;
};

#endif 
///////////////////////////////////////////////////////////
//  Context.cpp
//  Implementation of the Class Context
///////////////////////////////////////////////////////////

#include "Context.h"
#include "ConcreteStateA.h"

Context::Context()
{
	//default is A
	m_pState = ConcreteStateA::Instance();
}

Context::~Context()
{
}

void Context::changeState(State * st)
{
	m_pState = st;
}

void Context::request()
{
	m_pState->handle(this);
}
///////////////////////////////////////////////////////////
//  ConcreteStateA.h
//  Definition of the Class ConcreteStateA
///////////////////////////////////////////////////////////

#ifndef __CONCRETE_STATEA_H__
#define __CONCRETE_STATEA_H__

#include "State.h"

class ConcreteStateA : public State
{

public:
	virtual ~ConcreteStateA();
	
	static State * Instance();
	
	void handle(Context * c);

private:
	ConcreteStateA();
	static State * m_pState;
};

#endif 
///////////////////////////////////////////////////////////
//  ConcreteStateA.cpp
//  Implementation of the Class ConcreteStateA
///////////////////////////////////////////////////////////

#include "ConcreteStateA.h"
#include "ConcreteStateB.h"
#include "Context.h"
#include <iostream>

using namespace std;

State * ConcreteStateA::m_pState = NULL;

ConcreteStateA::ConcreteStateA()
{
}

ConcreteStateA::~ConcreteStateA()
{
}

State * ConcreteStateA::Instance()
{
	if(NULL == m_pState)
	{
		m_pState = new ConcreteStateA();
	}
	
	return m_pState;
}

void ConcreteStateA::handle(Context * c)
{
	cout << "Doing something in State A.\n Done, change state to B" << endl;
	c->changeState(ConcreteStateB::Instance());
}

Execution result:

Among the three roles of State mode, environment class, also known as context class, is an object with State. In environment class, it has an instance of State class, which defines the current State. When it is implemented, it is an object of a State subclass, which can define the initial State. Abstract State class defines an interface to encapsulate the behavior related to the specific State of the environment class. Concrete State class is a subclass of abstract State class. Each subclass implements a behavior related to a State of environment class. Each concrete State class corresponds to a concrete State of environment. The behavior of different concrete State classes is different.

5. Strategy pattern

Policy pattern, also known as policy pattern, defines a series of algorithms, encapsulates each algorithm, and enables them to replace each other. Policy patterns allow algorithms to change independently of their users.

Roles in policy mode:

(1) Environment class Context

(2) Abstract Strategy class

(3) Concrete strategy

Example code:

#include <iostream>
#include "Context.h"
#include "ConcreteStrategyA.h"
#include "ConcreteStrategyB.h"
#include "Strategy.h"
#include <vector>

using namespace std;

int main(int argc, char *argv[])
{
	Strategy * s1 = new ConcreteStrategyA();
	Context * cxt = new Context();
	cxt->setStrategy(s1);
	cxt->algorithm();

	Strategy *s2 = new ConcreteStrategyB();
	cxt->setStrategy(s2);
	cxt->algorithm();
	
	delete s1;
	delete s2;
		
	return 0;
}
///////////////////////////////////////////////////////////
//  Context.h
//  definition of the Class Context
///////////////////////////////////////////////////////////

#ifndef __CONTEXT_H__
#define __CONTEXT_H__

#include "Strategy.h"

class Context
{

public:
	Context();
	virtual ~Context();
	

	void algorithm();
	void setStrategy(Strategy* st);
	
private:
	Strategy *m_pStrategy;

};

#endif 
///////////////////////////////////////////////////////////
//  Context.cpp
//  Implementation of the Class Context
///////////////////////////////////////////////////////////

#include "Context.h"

Context::Context()
{
}

Context::~Context()
{
}

void Context::algorithm()
{
	m_pStrategy->algorithm();
}

void Context::setStrategy(Strategy* st)
{
	m_pStrategy = st;
}
///////////////////////////////////////////////////////////
//  ConcreteStrategyA.h
//  Definition of the Class ConcreteStrategyA
///////////////////////////////////////////////////////////

#ifndef __CONCRETE_STRATEGY_H__
#define __CONCRETE_STRATEGY_H__

#include "Strategy.h"

class ConcreteStrategyA : public Strategy
{

public:
	ConcreteStrategyA();
	virtual ~ConcreteStrategyA();

	void algorithm();

};

#endif
///////////////////////////////////////////////////////////
//  ConcreteStrategyA.cpp
//  Implementation of the Class ConcreteStrategyA
///////////////////////////////////////////////////////////

#include "ConcreteStrategyA.h"
#include <iostream>

using namespace std;

ConcreteStrategyA::ConcreteStrategyA()
{

}

ConcreteStrategyA::~ConcreteStrategyA()
{

}

void ConcreteStrategyA::algorithm()
{
	cout << "use algorithm A" << endl;
}

Execution result:

In the above roles, the environment class can adopt multiple strategies when solving a problem, and maintain a reference instance of the abstract policy class in the environment class; the abstract policy class declares the abstract method for the supported algorithm, which is the base class of all policy classes. The concrete policy class implements the algorithm defined in the abstract policy class.

The strategy mode can support the "open close principle" well, and can change the algorithm or add new algorithm without modifying the original system.

 

reference material: https://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html

58 original articles published, 61 praised, 130000 visitors+
Private letter follow

Topics: network