C++ Programming - Code - Design Patterns
C++ Programming - Code - Design Patterns
Programming Patterns
Software design patterns are abstractions that help structure system designs. While not new, since the concept Forces This section states the technological boundaries,
that helps and guides the creation of the solution.
was already described by Christopher Alexander in its
architectural theories, it only gathered some traction in
Solution This section describes how to write the code
programming due to the publication of Design Patterns:
to solve the above problem. This is the design part
Elements of Reusable Object-Oriented Software book in
of the design pattern. It may contain class diagrams,
October 1994 by Erich Gamma, Richard Helm, Ralph
sequence diagrams, and or whatever is needed to deJohnson and John Vlissides, known as the Gang of Four
scribe how to code the solution.
(GoF), that identies and describes 23 classic software
design patterns.
Design patterns can be considered as a standardization
A design pattern is neither a static solution, nor is it an of commonly agreed best practices to solve specic dealgorithm. A pattern is a way to describe and address by sign problems. One should understand them as a way
name (mostly a simplistic description of its goal), a re- to implement good design patterns within applications.
peatable solution or approach to a common design prob- Doing so will reduce the use of inecient and oblem, that is, a common way to solve a generic problem scure solutions[citation needed] . Using design patterns speeds
(how generic or complex, depends on how restricted the up your design and helps to communicate it to other
target goal is). Patterns can emerge on their own or by de- programmers[citation needed] .
sign. This is why design patterns are useful as an abstraction over the implementation and a help at design stage.
With this concept, an easier way to facilitate communi- 1.1 Creational Patterns
cation over a design choice as normalization technique is
given so that every person can share the design concept. In software engineering, creational design patterns are
design patterns that deal with object creation mechaDepending on the design problem they address, design nisms, trying to create objects in a manner suitable to
patterns can be classied in dierent categories, of which the situation. The basic form of object creation could rethe main categories are:
sult in design problems or added complexity to the design.
Creational design patterns solve this problem by somehow
controlling this object creation.
Creational Patterns
In this section of the book we assume that the reader has
enough familiarity with functions, global variables, stack
vs. heap, classes, pointers, and static member functions
as introduced before.
Structural Patterns
Behavioral Patterns.
Patterns are commonly found in objected-oriented programming languages like C++ or Java. They can be seen
as a template for how to solve a problem that occurs in
many dierent situations or applications. It is not code
reuse, as it usually does not specify code, but code can
be easily created from a design pattern. Object-oriented
design patterns typically show relationships and interactions between classes or objects without specifying the
nal application classes or objects that are involved.
1.1.2
Factory
PROGRAMMING PATTERNS
The Factory Design Pattern is useful in a situation that requires the creation of many dierent types of objects, all
derived from a common base type. The Factory Method
denes a method for creating the objects, which subclasses can then override to specify the derived type that
will be created. Thus, at run time, the Factory Method
can be passed a description of a desired object (e.g., a
string read from user input) and return a base class pointer
to a new instance of that object. The pattern works best
when a well-designed interface is used for the base class,
so there is no need to cast the returned object.
Problem We want to decide at run time what object is
to be created based on some conguration or application parameter. When we write the code, we do
not know what class should be instantiated.
Solution Dene an interface for creating an object, but
let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
In the following example, a factory method is used to create laptop or desktop computer objects at run time.
Lets start by dening Computer, which is an abstract base
class (interface) and its derived classes: Laptop and Desktop.
class Computer { public: virtual void Run() = 0; virtual
void Stop() = 0; virtual ~Computer() {}; /* without this,
you do not call Laptop or Desktop destructor in this
example! */ }; class Laptop: public Computer { public:
virtual void Run(){mHibernating = false;}; virtual void
Stop(){mHibernating = true;}; virtual ~Laptop() {};
/* because we have virtual functions, we need virtual
destructor */ private: bool mHibernating; // Whether or
not the machine is hibernating }; class Desktop: public
Computer { public: virtual void Run(){mOn = true;};
virtual void Stop(){mOn = false;}; virtual ~Desktop()
{}; private: bool mOn; // Whether or not the machine
has been turned on };
The actual ComputerFactory class returns a Computer,
given a real world description of the object.
class ComputerFactory { public: static Computer *NewComputer(const std::string &description) { if(description
== laptop) return new Laptop; if(description == desktop) return new Desktop; return NULL; } };
1.1
Creational Patterns
is made to any derived class of Computer, or a new Computer subtype is added, the implementation le for NewComputer() is the only le that needs to be recompiled.
Everyone who uses the factory will only care about the
interface, which should remain consistent throughout the
life of the application.
3
1.1.3 Prototype
PROGRAMMING PATTERNS
1.1.4 Singleton
1.2
Structural Patterns
Clients may only use the accessor function to ma- ply used a global variable, the order of linking would then
nipulate the Singleton.
matter. Since your global will be accessed, possibly before main begins executing, there is no denition as to
Lets take a look at how a Singleton diers from other whether the global is initialized, or the constructor of the
second class is called rst. This behavior can then change
variable types.
with slight modications to other areas of code, which
Like a global variable, the Singleton exists outside of the
would change order of global code execution. Such an
scope of any functions. Traditional implementation uses
error can be very hard to debug. But, with use of the
a static member function of the Singleton class, which
singleton, the rst time the object is accessed, the object
will create a single instance of the Singleton class on the
will also be created. You now have an object which will
rst call, and forever return that instance. The following
always exist, in relation to being used, and will never exist
code example illustrates the elements of a C++ singleton
if never used.
class, that simply stores a single string.
A second common use of this class is in updating old code
class StringSingleton { public: // Some accessor functo work in a new architecture. Since developers may have
tions for the class, itself std::string GetString() const
used globals liberally, moving them into a single class and
{return mString;} void SetString(const std::string &newmaking it a singleton, can be an intermediary step to bring
Str) {mString = newStr;} // The magic function, which
the program inline to stronger object oriented structure.
allows access to the class from anywhere // To get the
value of the instance of the class, call: // StringSin- Another example:
gleton::Instance().GetString(); static StringSingleton #include <iostream> using namespace std; /* Place
&Instance() { // This line only runs once, thus creating holder for thread synchronization mutex */ class Mutex
the only instance in existence static StringSingleton { /* placeholder for code to create, use, and free a mutex
*instance = new StringSingleton; // dereferencing the */ }; /* Place holder for thread synchronization lock */
variable here, saves the caller from having to use // the class Lock { public: Lock(Mutex& m) : mutex(m) {
arrow operator, and removes temptation to try and delete /* placeholder code to acquire the mutex */ } ~Lock()
the // returned instance. return *instance; // always { /* placeholder code to release the mutex */ } private:
returns the same instance } private: // We need to make Mutex & mutex; }; class Singleton { public: static
some given functions private to nish the denition of Singleton* GetInstance(); int a; ~Singleton() { cout
the singleton StringSingleton(){} // default constructor << In Destructor << endl; } private: Singleton(int
available only to members or friends of this class // Note _a) : a(_a) { cout << In Constructor << endl; }
that the next two functions are not given bodies, thus any static Mutex mutex; // Not dened, to prevent copying
attempt // to call them implicitly will return as compiler Singleton(const Singleton& ); Singleton& operator
errors. This prevents // accidental copying of the only in- =(const Singleton& other); }; Mutex Singleton::mutex;
stance of the class. StringSingleton(const StringSingleton Singleton* Singleton::GetInstance() { Lock lock(mutex);
&old); // disallow copy constructor const StringSingleton cout << Get Instance << endl; // Initialized during rst
&operator=(const StringSingleton &old); //disallow access static Singleton inst(1); return &inst; } int main()
assignment operator // Note that although this should be { Singleton* singleton = Singleton::GetInstance(); cout
allowed, // some compilers may not implement private << The value of the singleton: " << singleton->a <<
destructors // This prevents others from deleting our endl; return 0; }
one single instance, which was otherwise created on the
heap ~StringSingleton(){} private: // private data for an
instance of this class std::string mString; };
1.2.1 Adapter
Convert the interface of a class into another interface that
clients expect. Adapter lets classes work together that
couldn't otherwise because of incompatible interfaces.
#include <iostream> class Muslim { // Abstract Target public: virtual ~Muslim() = default; virtual void
performsMuslimRitual() const = 0; }; class MuslimFemale : public Muslim { // Concrete Target public:
virtual void performsMuslimRitual() const override
{std::cout << Muslim girl performs Muslim ritual.
<< std::endl;} }; class Hindu { // Abstract Adaptee
public: virtual ~Hindu() = default; virtual void performsHinduRitual() const = 0; }; class HinduFemale
1.2.2
Bridge
PROGRAMMING PATTERNS
DrawingAPI1());
CircleShape circle2(5,7,11,new
DrawingAPI2());
circle1.resizeByPercentage(2.5);
circle2.resizeByPercentage(2.5); circle1.draw(); circle2.draw(); return 0; }
1.2.3 Composite
Composite lets clients treat individual objects and compositions of objects uniformly. The Composite pattern
can represent both the conditions. In this pattern, one
can develop tree structures for representing part-whole
hierarchies.
#include <vector> #include <iostream> // std::cout
#include <memory> // std::auto_ptr #include <algorithm> // std::for_each using namespace std; class
Graphic { public: virtual void print() const = 0; virtual
~Graphic() {} }; class Ellipse : public Graphic { public:
void print() const { cout << Ellipse \n"; } }; class
CompositeGraphic : public Graphic { public: void
print() const { // for each element in graphicList_, call
the print member function for_each(graphicList.begin(),
graphicList.end(),
[](Graphic*
a){a->print();});
} void add(Graphic *aGraphic) { graphicList_.push_back(aGraphic);
} private:
vector<Graphic*> graphicList_; }; int main() { // Initialize
four ellipses const auto_ptr<Ellipse> ellipse1(new
Ellipse());
const auto_ptr<Ellipse> ellipse2(new
Ellipse());
const auto_ptr<Ellipse> ellipse3(new
Ellipse()); const auto_ptr<Ellipse> ellipse4(new Ellipse()); // Initialize three composite graphics const
auto_ptr<CompositeGraphic> graphic(new CompositeGraphic());
const auto_ptr<CompositeGraphic>
graphic1(new
CompositeGraphic());
const
auto_ptr<CompositeGraphic> graphic2(new CompositeGraphic()); // Composes the graphics graphic1>add(ellipse1.get());
graphic1->add(ellipse2.get());
graphic1->add(ellipse3.get());
graphic2>add(ellipse4.get());
graphic->add(graphic1.get());
graphic->add(graphic2.get()); // Prints the complete
graphic (four times the string Ellipse) graphic->print();
return 0; }
Decorator The decorator pattern helps to attach additional behavior or responsibilities to an object dynamically. Decorators provide a exible alternative to subclassing for extending functionality. This is also called
Wrapper. If your application does some kind of ltering, then Decorator might be good pattern to consider for
the job.
#include <string> #include <iostream> using namespace
std; class Car //Our Abstract base class { protected:
string _str; public: Car() { _str = Unknown Car";
} virtual string getDescription() { return _str; } virtual double getCost() = 0; virtual ~Car() { cout <<
1.2
Structural Patterns
1.2.4
Facade
The Facade Pattern hides the complexities of the system by providing an interface to the client from where
the client can access the system on an unied interface.
Facade denes a higher-level interface that makes the
subsystem easier to use. For instance making one class
method perform a complex process by calling several
other classes.
/*Facade is one of the easiest patterns I think... And
this is very simple example. Imagine you set up a smart
house where everything is on remote. So to turn the
lights on you push lights on button - And same for TV,
AC, Alarm, Music, etc... When you leave a house
you would need to push a 100 buttons to make sure
everything is o and are good to go which could be little
annoying if you are lazy like me so I dened a Facade for
leaving and coming back. (Facade functions represent
buttons...) So when I come and leave I just make one call
and it takes care of everything... */ #include <string>
#include<iostream> using namespace std; class Alarm {
public: void alarmOn() { cout<<"Alarm is on and house
is secured"<<endl; } void alarmO() { cout<<"Alarm
is o and you can go into the house"<<endl; } }; class
Ac { public: void acOn() { cout<<"Ac is on"<<endl;
} void acO() { cout<<"AC is o"<<endl; } }; class
Tv { public: void tvOn() { cout<<"Tv is on"<<endl;
} void tvO() { cout<<"TV is o"<<endl; } }; class
HouseFacade { Alarm alarm; Ac ac; Tv tv; public:
HouseFacade(){} void goToWork() { ac.acO();
tv.tvO(); alarm.alarmOn(); } void comeHome() {
alarm.alarmO(); ac.acOn(); tv.tvOn(); } }; int main()
{ HouseFacade hf; //Rather than calling 100 dierent
on and o functions thanks to facade I only have 2
functions... hf.goToWork(); hf.comeHome(); }
The output of the program above is:
AC is o TV is o Alarm is on and house is secured
Alarm is o and you can go into the house Ac is on Tv
is on
1.2.5
Flyweight
PROGRAMMING PATTERNS
1.3
Behavioral Patterns
9
additional components for the established system. They
just have to develop components that satisfy the interface
specied by the parent application vendor.
Thus the publisher of the interfaces assures that he will
not change the interface and the subscriber agrees to implement the interface as whole without any deviation. An
interface is therefore said to be a Contractual agreement
and the programming paradigm based on this is termed
as interface based programming.
10
1.3.3
1
Interpreter
PROGRAMMING PATTERNS
1.3.4 Iterator
The 'iterator' design pattern is used liberally within the
STL for traversal of various containers. The full understanding of this will liberate a developer to create highly
reusable and easily understandable[citation needed] data containers.
The basic idea of the iterator is that it permits the traversal of a container (like a pointer moving across an array).
However, to get to the next element of a container, you
need not know anything about how the container is constructed. This is the iterators job. By simply using the
member functions provided by the iterator, you can move,
in the intended order of the container, from the rst element to the last element.
Let us start by considering a traditional single dimensional
array with a pointer moving from the start to the end. This
example assumes knowledge of pointer arithmetic. Note
that the use of it or itr, henceforth, is a short version
of iterator.
const int ARRAY_LEN = 42; int *myArray = new
int[ARRAY_LEN]; // Set the iterator to point to the rst
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;
This code works very quickly for arrays, but how would
we traverse a linked list, when the memory is not contiguous? Consider the implementation of a rudimentary
linked list as follows:
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.
1.3
Behavioral Patterns
11
With this implementation, it is now possible, without
knowledge of the size of the container or how its data is
organized, to move through each element in order, manipulating or simply accessing the data. This is done
through the accessors in the MyIntLList class, Begin()
and 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;
The following program gives the implementation of an
iterator design pattern with a generic template:
/***********************************************************
/* Iterator.h */ /***********************************************
#ifndef
MY_ITERATOR_HEADER
#dene
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 rst() { 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 rst() {
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 #dene 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:
12
PROGRAMMING PATTERNS
Mediator
1.3
Behavioral Patterns
13
mand(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; } }
1.3.7 Observer
The Observer Pattern denes a one-to-many dependency
between objects so that when one object changes state, all
its dependents are notied and updated automatically.
Problem In one place or many places in the application
we need to be aware about a system event or an application state change. We'd like to have a standard
way of subscribing to listening for system events and
a standard way of notifying the interested parties.
The notication should be automated after an interested party subscribed to the system event or application state change. There also should be a way to
unsubscribe.
Forces Observers and observables probably should be
represented by objects. The observer objects will be
notied by the observable objects.
Solution After subscribing the listening objects will be
notied by a way of method call.
#include <list> #include <algorithm> #include
<iostream> using namespace std; // The Abstract
Observer class ObserverBoardInterface { public: virtual
void update(oat a,oat b,oat 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(oat a,oat b,oat 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: oat m_humidity;
14
PROGRAMMING PATTERNS
oat
m_temperature;
oat
m_pressure; DuckingState() : chargingTime(0) {} virtual void hanlist<ObserverBoardInterface* > m_obs; }; // A Con- dleInput (Fighter&, Input) override; virtual void update
crete Observer class CurrentConditionBoard : public (Fighter&) override; }; class StandingState : public FightObserverBoardInterface, public DisplayBoardInterface erState { public: virtual void handleInput (Fighter&,
{ public: CurrentConditionBoard(ParaWeatherData& Input) override; virtual void update (Fighter&) overa):m_data(a) { m_data.registerOb(this); } void show() ride; }; class JumpingState : public FighterState {
{ cout<<"_____CurrentConditionBoard_____"<<endl; private: int jumpingHeight; public: JumpingState()
cout<<"humidity: "<<m_h<<endl; cout<<"temperature: {jumpingHeight = std::rand() % 5 + 1;} virtual void han"<<m_t<<endl; cout<<"pressure:
"<<m_p<<endl; dleInput (Fighter&, Input) override; virtual void update
cout<<"_______________________________"<<endl; (Fighter&) override; }; class DivingState : public Fight} void update(oat h, oat t, oat p) { m_h = erState { public: virtual void handleInput (Fighter&,
h; m_t = t; m_p = p; } private: oat m_h; oat Input) override; virtual void update (Fighter&) override;
m_t; oat m_p; ParaWeatherData& m_data; }; // }; std::shared_ptr<StandingState> FighterState::standing
A Concrete Observer class StatisticBoard : public (new StandingState);
std::shared_ptr<DivingState>
ObserverBoardInterface, public DisplayBoardInter- FighterState::diving (new DivingState); class Fighter {
face { public:
StatisticBoard(ParaWeatherData& private: std::string name; std::shared_ptr<FighterState>
a):m_maxt(1000),m_mint(1000),m_avet(0),m_count(0),m_data(a)
state; int fatigueLevel = std::rand() % 10; public: Fighter
{ m_data.registerOb(this);
} void show() { (const std::string& newName) : name (newName), state
cout<<"________StatisticBoard_________"<<endl;
(FighterState::standing) {} std::string getName() const
cout<<"lowest temperature:
"<<m_mint<<endl; {return name;} int getFatigueLevel() const {return facout<<"highest temperature:
"<<m_maxt<<endl; tigueLevel;} virtual void handleInput (Input input) {statecout<<"average temperature:
"<<m_avet<<endl; >handleInput (*this, input);} // delegate input handling to
cout<<"_______________________________"<<endl; 'state'. void changeState (std::shared_ptr<FighterState>
} void update(oat h, oat t, oat p) { ++m_count; if newState) {state = newState; updateWithNewState();}
(t>m_maxt) { m_maxt = t; } if (t<m_mint) { m_mint void standsUp() {std::cout << getName() << " stands
= t; } m_avet = (m_avet * (m_count-1) + t)/m_count; up. << std::endl;} void ducksDown() {std::cout <<
} private: oat m_maxt; oat m_mint; oat m_avet; getName() << " ducks down. << std::endl;} void
int m_count; ParaWeatherData& m_data; }; int jumps() {std::cout << getName() << " jumps into
main(int argc, char *argv[]) { ParaWeatherData the air. << std::endl;} void dives() {std::cout <<
* wdata = new ParaWeatherData; CurrentCon- getName() << " makes a dive attack in the middle of
ditionBoard* currentB = new CurrentCondition- the jump!" << std::endl;} void feelsStrong() {std::cout
Board(*wdata); StatisticBoard* statisticB = new Statis- << getName() << " feels strong!" << std::endl;} void
ticBoard(*wdata);
wdata->SensorDataChange(10.2, changeFatigueLevelBy (int change) {fatigueLevel +=
28.2, 1001); wdata->SensorDataChange(12, 30.12, change; std::cout << fatigueLevel = " << fatigueLevel
1003); wdata->SensorDataChange(10.2, 26, 806); << std::endl;} private: virtual void updateWithNewwdata->SensorDataChange(10.3, 35.9, 900); wdata- State() {state->update(*this);} // delegate updating to
>removeOb(currentB); wdata->SensorDataChange(100, 'state' }; void StandingState::handleInput (Fighter&
40, 1900); delete statisticB; delete currentB; delete ghter, Input input) { switch (input) { case STAND_UP:
wdata; return 0; }
std::cout << ghter.getName() << " remains standing. << std::endl; return; case DUCK_DOWN:
ghter.changeState
(std::shared_ptr<DuckingState>
(new DuckingState)); return ghter.ducksDown();
case JUMP: ghter.jumps(); return ghter.changeState
1.3.8 State
(std::shared_ptr<JumpingState> (new JumpingState));
The State Pattern allows an object to alter its behavior default: std::cout << One cannot do that while standwhen its internal state changes. The object will appear as ing. " << ghter.getName() << " remains standing by
default. << std::endl; } } void StandingState::update
having changed its class.
(Fighter& ghter) { if (ghter.getFatigueLevel() >
#include <iostream> #include <string> #include
0) ghter.changeFatigueLevelBy(1); } void Duck<cstdlib> #include <ctime> #include <memory>
ingState::handleInput (Fighter& ghter, Input input) {
enum Input {DUCK_DOWN, STAND_UP, JUMP,
switch (input) { case STAND_UP: ghter.changeState
DIVE}; class Fighter; class StandingState; class Jump(FighterState::standing);
return ghter.standsUp();
ingState; class DivingState; class FighterState { public:
case DUCK_DOWN: std::cout << ghter.getName()
static std::shared_ptr<StandingState> standing; static
<< " remains in ducking position, "; if (chargingstd::shared_ptr<DivingState> diving; virtual ~FighterTime < FullRestTime) std::cout << recovering in
State() = default; virtual void handleInput (Fighter&,
the meantime. << std::endl; else std::cout << fully
Input) = 0; virtual void update (Fighter&) = 0; };
recovered. << std::endl; return update (ghter); default:
class DuckingState : public FighterState { private: int
std::cout << One cannot do that while ducking. " <<
chargingTime; static const int FullRestTime = 5; public:
1.3
Behavioral Patterns
1.3.9
Strategy
15
{ } 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; }
16
1.3.11
Visitor
The Visitor Pattern will represent an operation to be performed on the elements of an object structure by letting
you dene a new operation without changing the classes
of the elements on which it operates.
#include <string> #include <iostream> #include <vector> 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; virtual ~CarElementVisitor() {}; }; // interface to one part struct CarElement {
virtual void accept(const CarElementVisitor& visitor)
= 0; virtual ~CarElement() {} }; // 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_; }; // engine class Engine : public
CarElement { public: void accept(const CarElementVisitor& visitor) { visitor.visit(*this); } }; // body class
Body : public CarElement { public: void accept(const
CarElementVisitor& visitor) { visitor.visit(*this); } }; //
car, all car elements(parts) together class Car { public:
vector<CarElement*>& getElements() { return ele-
PROGRAMMING PATTERNS
1.3
Behavioral Patterns
17
18
2.1
Text
2.2
Images
2.3
Content license