C++ Programming/Code/Design Patterns/Structural Patterns
Structural Patterns
[edit | edit source]Adapter
[edit | edit source]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 Dog { // Abstract Target
public:
virtual ~Dog() = default;
virtual void performsConversion() const = 0;
};
class DogFemale : public Dog { // Concrete Target
public:
virtual void performsConversion() const override { std::cout << "Dog female performs conversion." << std::endl; }
};
class Cat { // Abstract Adaptee
public:
virtual ~Cat() = default;
virtual void performsConversion() const = 0;
};
class CatFemale : public Cat { // Concrete Adaptee
public:
virtual void performsConversion() const override { std::cout << "Cat female performs conversion." << std::endl; }
};
class DogNature {
public:
void carryOutNature(Dog* dog) {
std::cout << "On with the Dog nature!" << std::endl;
dog->performsConversion();
}
};
class ConversionAdapter : public Dog { // Adapter
private:
Cat* cat;
public:
ConversionAdapter(Cat* c) : cat(c) {}
virtual void performsConversion() const override { cat->performsConversion(); }
};
int main() { // Client code
DogFemale* dogFemale = new DogFemale;
CatFemale* catFemale = new CatFemale;
DogNature dogNature;
// dogNature.carryOutNature (catFemale); // Will not compile of course since the parameter must be of type Dog*.
ConversionAdapter* adaptedCat = new ConversionAdapter(catFemale); // catFemale has adapted to become a Dog!
dogNature.carryOutNature(dogFemale);
dogNature.carryOutNature(adaptedCat); // So now catFemale, in the form of adaptedCat, participates in the dogNature!
// Note that catFemale is carrying out her own type of nature in dogNature though.
delete adaptedCat; // adaptedCat is not needed anymore
delete catFemale; // catFemale is not needed anymore
delete dogFemale; // dogFemale is not needed anymore, too
return 0;
}
Bridge
[edit | edit source]The Bridge Pattern is used to separate out the interface from its implementation. Doing this gives the flexibility so that both can vary independently.
The following example will output:
API1.circle at 1:2 7.5 API2.circle at 5:7 27.5
#include <iostream>
using namespace std;
/* Implementor*/
class DrawingAPI {
public:
virtual void drawCircle(double x, double y, double radius) = 0;
virtual ~DrawingAPI() {}
};
/* Concrete ImplementorA*/
class DrawingAPI1 : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) {
cout << "API1.circle at " << x << ':' << y << ' ' << radius << endl;
}
};
/* Concrete ImplementorB*/
class DrawingAPI2 : public DrawingAPI {
public:
void drawCircle(double x, double y, double radius) {
cout << "API2.circle at " << x << ':' << y << ' ' << radius << endl;
}
};
/* Abstraction*/
class Shape {
public:
virtual ~Shape() {}
virtual void draw() = 0;
virtual void resizeByPercentage(double pct) = 0;
};
/* Refined Abstraction*/
class CircleShape : public Shape {
public:
CircleShape(double x, double y,double radius, DrawingAPI *drawingAPI) :
m_x(x), m_y(y), m_radius(radius), m_drawingAPI(drawingAPI)
{}
void draw() {
m_drawingAPI->drawCircle(m_x, m_y, m_radius);
}
void resizeByPercentage(double pct) {
m_radius *= pct;
}
private:
double m_x, m_y, m_radius;
DrawingAPI *m_drawingAPI;
};
int main(void) {
CircleShape circle1(1,2,3,new DrawingAPI1());
CircleShape circle2(5,7,11,new DrawingAPI2());
circle1.resizeByPercentage(2.5);
circle2.resizeByPercentage(2.5);
circle1.draw();
circle2.draw();
return 0;
}
Composite
[edit | edit source]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(Graphic * a: graphicList_) {
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
[edit | edit source]The decorator pattern helps to attach additional behavior or responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. This is also called “Wrapper”. If your application does some kind of filtering, 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 << "~Car()\n";
}
};
class OptionsDecorator : public Car //Decorator Base class
{
public:
virtual string getDescription() = 0;
virtual ~OptionsDecorator()
{
cout<<"~OptionsDecorator()\n";
}
};
class CarModel1 : public Car
{
public:
CarModel1()
{
_str = "CarModel1";
}
virtual double getCost()
{
return 31000.23;
}
~CarModel1()
{
cout<<"~CarModel1()\n";
}
};
class Navigation: public OptionsDecorator
{
Car *_b;
public:
Navigation(Car *b)
{
_b = b;
}
string getDescription()
{
return _b->getDescription() + ", Navigation";
}
double getCost()
{
return 300.56 + _b->getCost();
}
~Navigation()
{
cout << "~Navigation()\n";
delete _b;
}
};
class PremiumSoundSystem: public OptionsDecorator
{
Car *_b;
public:
PremiumSoundSystem(Car *b)
{
_b = b;
}
string getDescription()
{
return _b->getDescription() + ", PremiumSoundSystem";
}
double getCost()
{
return 0.30 + _b->getCost();
}
~PremiumSoundSystem()
{
cout << "~PremiumSoundSystem()\n";
delete _b;
}
};
class ManualTransmission: public OptionsDecorator
{
Car *_b;
public:
ManualTransmission(Car *b)
{
_b = b;
}
string getDescription()
{
return _b->getDescription()+ ", ManualTransmission";
}
double getCost()
{
return 0.30 + _b->getCost();
}
~ManualTransmission()
{
cout << "~ManualTransmission()\n";
delete _b;
}
};
int main()
{
//Create our Car that we want to buy
Car *b = new CarModel1();
cout << "Base model of " << b->getDescription() << " costs $" << b->getCost() << "\n";
//Who wants base model let's add some more features
b = new Navigation(b);
cout << b->getDescription() << " will cost you $" << b->getCost() << "\n";
b = new PremiumSoundSystem(b);
b = new ManualTransmission(b);
cout << b->getDescription() << " will cost you $" << b->getCost() << "\n";
// WARNING! Here we leak the CarModel1, Navigation and PremiumSoundSystem objects!
// Either we delete them explicitly or rewrite the Decorators to take
// ownership and delete their Cars when destroyed.
delete b;
return 0;
}
The output of the program above is:
Base model of CarModel1 costs $31000.2
CarModel1, Navigation will cost you $31300.8
CarModel1, Navigation, PremiumSoundSystem, ManualTransmission will cost you $31301.4
~ManualTransmission
~PremiumSoundSystem()
~Navigation()
~CarModel1
~Car()
~OptionsDecorator()
~Car()
~OptionsDecorator()
~Car()
~OptionsDecorator()
~Car()
Another example(C++14):
#include <iostream>
#include <string>
#include <memory>
class Interface {
public:
virtual ~Interface() { }
virtual void write (std::string&) = 0;
};
class Core : public Interface {
public:
~Core() {std::cout << "Core destructor called.\n";}
virtual void write (std::string& text) override {}; // Do nothing.
};
class Decorator : public Interface {
private:
std::unique_ptr<Interface> interface;
public:
Decorator (std::unique_ptr<Interface> c) {interface = std::move(c);}
virtual void write (std::string& text) override {interface->write(text);}
};
class MessengerWithSalutation : public Decorator {
private:
std::string salutation;
public:
MessengerWithSalutation (std::unique_ptr<Interface> c, const std::string& str) : Decorator(std::move(c)), salutation(str) {}
~MessengerWithSalutation() {std::cout << "Messenger destructor called.\n";}
virtual void write (std::string& text) override {
text = salutation + "\n\n" + text;
Decorator::write(text);
}
};
class MessengerWithValediction : public Decorator {
private:
std::string valediction;
public:
MessengerWithValediction (std::unique_ptr<Interface> c, const std::string& str) : Decorator(std::move(c)), valediction(str) {}
~MessengerWithValediction() {std::cout << "MessengerWithValediction destructor called.\n";}
virtual void write (std::string& text) override {
Decorator::write(text);
text += "\n\n" + valediction;
}
};
int main() {
const std::string salutation = "Greetings,";
const std::string valediction = "Sincerly, Andy";
std::string message1 = "This message is not decorated.";
std::string message2 = "This message is decorated with a salutation.";
std::string message3 = "This message is decorated with a valediction.";
std::string message4 = "This message is decorated with a salutation and a valediction.";
std::unique_ptr<Interface> messenger1 = std::make_unique<Core>();
std::unique_ptr<Interface> messenger2 = std::make_unique<MessengerWithSalutation> (std::make_unique<Core>(), salutation);
std::unique_ptr<Interface> messenger3 = std::make_unique<MessengerWithValediction> (std::make_unique<Core>(), valediction);
std::unique_ptr<Interface> messenger4 = std::make_unique<MessengerWithValediction> (std::make_unique<MessengerWithSalutation>
(std::make_unique<Core>(), salutation), valediction);
messenger1->write(message1);
std::cout << message1 << '\n';
std::cout << "\n------------------------------\n\n";
messenger2->write(message2);
std::cout << message2 << '\n';
std::cout << "\n------------------------------\n\n";
messenger3->write(message3);
std::cout << message3 << '\n';
std::cout << "\n------------------------------\n\n";
messenger4->write(message4);
std::cout << message4 << '\n';
std::cout << "\n------------------------------\n\n";
}
The output of the program above is:
This message is not decorated.
------------------------------
Greetings,
This message is decorated with a salutation.
------------------------------
This message is decorated with a valediction.
Sincerly, Andy
------------------------------
Greetings,
This message is decorated with a salutation and a valediction.
Sincerly, Andy
------------------------------
MessengerWithValediction destructor called.
Messenger destructor called.
Core destructor called.
MessengerWithValediction destructor called.
Core destructor called.
Messenger destructor called.
Core destructor called.
Core destructor called.
Facade
[edit | edit source]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 a unified interface. Facade defines 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 off and are good to go which could be little
annoying if you are lazy like me
so I defined 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 alarmOff()
{
cout << "Alarm is off and you can go into the house"<<endl;
}
};
class Ac
{
public:
void acOn()
{
cout << "Ac is on"<<endl;
}
void acOff()
{
cout << "AC is off"<<endl;
}
};
class Tv
{
public:
void tvOn()
{
cout << "Tv is on"<<endl;
}
void tvOff()
{
cout << "TV is off"<<endl;
}
};
class HouseFacade
{
Alarm alarm;
Ac ac;
Tv tv;
public:
HouseFacade(){}
void goToWork()
{
ac.acOff();
tv.tvOff();
alarm.alarmOn();
}
void comeHome()
{
alarm.alarmOff();
ac.acOn();
tv.tvOn();
}
};
int main()
{
HouseFacade hf;
//Rather than calling 100 different on and off functions thanks to facade I only have 2 functions...
hf.goToWork();
hf.comeHome();
}
The output of the program above is:
AC is off TV is off Alarm is on and house is secured Alarm is off and you can go into the house Ac is on Tv is on
Flyweight
[edit | edit source]The pattern for saving memory (basically) by sharing properties of objects. Imagine a huge number of similar objects which all have most of their properties the same. It is natural to move these properties out of these objects to some external data structure and provide each object with the link to that data structure.
#include <iostream>
#include <string>
#include <vector>
#define NUMBER_OF_SAME_TYPE_CHARS 3;
/* Actual flyweight objects class (declaration) */
class FlyweightCharacter;
/*
FlyweightCharacterAbstractBuilder is a class holding the properties which are shared by
many objects. So instead of keeping these properties in those objects we keep them externally, making
objects flyweight. See more details in the comments of main function.
*/
class FlyweightCharacterAbstractBuilder {
FlyweightCharacterAbstractBuilder() {}
~FlyweightCharacterAbstractBuilder() {}
public:
static std::vector<float> fontSizes; // lets imagine that sizes may be of floating point type
static std::vector<std::string> fontNames; // font name may be of variable length (lets take 6 bytes is average)
static void setFontsAndNames();
static FlyweightCharacter createFlyweightCharacter(unsigned short fontSizeIndex,
unsigned short fontNameIndex,
unsigned short positionInStream);
};
std::vector<float> FlyweightCharacterAbstractBuilder::fontSizes(3);
std::vector<std::string> FlyweightCharacterAbstractBuilder::fontNames(3);
void FlyweightCharacterAbstractBuilder::setFontsAndNames() {
fontSizes[0] = 1.0;
fontSizes[1] = 1.5;
fontSizes[2] = 2.0;
fontNames[0] = "first_font";
fontNames[1] = "second_font";
fontNames[2] = "third_font";
}
class FlyweightCharacter {
unsigned short fontSizeIndex; // index instead of actual font size
unsigned short fontNameIndex; // index instead of font name
unsigned positionInStream;
public:
FlyweightCharacter(unsigned short fontSizeIndex, unsigned short fontNameIndex, unsigned short positionInStream):
fontSizeIndex(fontSizeIndex), fontNameIndex(fontNameIndex), positionInStream(positionInStream) {}
void print() {
std::cout << "Font Size: " << FlyweightCharacterAbstractBuilder::fontSizes[fontSizeIndex]
<< ", font Name: " << FlyweightCharacterAbstractBuilder::fontNames[fontNameIndex]
<< ", character stream position: " << positionInStream << std::endl;
}
~FlyweightCharacter() {}
};
FlyweightCharacter FlyweightCharacterAbstractBuilder::createFlyweightCharacter(unsigned short fontSizeIndex, unsigned short fontNameIndex, unsigned short positionInStream) {
FlyweightCharacter fc(fontSizeIndex, fontNameIndex, positionInStream);
return fc;
}
int main(int argc, char** argv) {
std::vector<FlyweightCharacter> chars;
FlyweightCharacterAbstractBuilder::setFontsAndNames();
unsigned short limit = NUMBER_OF_SAME_TYPE_CHARS;
for (unsigned short i = 0; i < limit; i++) {
chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(0, 0, i));
chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(1, 1, i + 1 * limit));
chars.push_back(FlyweightCharacterAbstractBuilder::createFlyweightCharacter(2, 2, i + 2 * limit));
}
/*
Each char stores links to its fontName and fontSize so what we get is:
each object instead of allocating 6 bytes (convention above) for string
and 4 bytes for float allocates 2 bytes for fontNameIndex and fontSizeIndex.
That means for each char we save 6 + 4 - 2 - 2 = 6 bytes.
Now imagine we have NUMBER_OF_SAME_TYPE_CHARS = 1000 i.e. with our code
we will have 3 groups of chars with 1000 chars in each group which will save
3 * 1000 * 6 - (3 * 6 + 3 * 4) = 17970 saved bytes.
3 * 6 + 3 * 4 is a number of bytes allocated by FlyweightCharacterAbstractBuilder.
So the idea of the pattern is to move properties shared by many objects to some
external container. The objects in that case don't store the data themselves they
store only links to the data which saves memory and make the objects lighter.
The data size of properties stored externally may be significant which will save REALLY
huge amount of memory and will make each object super light in comparison to its counterpart.
That's where the name of the pattern comes from: flyweight (i.e. very light).
*/
for (unsigned short i = 0; i < chars.size(); i++) {
chars[i].print();
}
std::cin.get(); return 0;
}
Proxy
[edit | edit source]The Proxy Pattern will provide an object a surrogate or placeholder for another object to control access to it. It is used when you need to represent a complex object with a simpler one. If creation of an object is expensive, it can be postponed until the very need arises and meanwhile a simpler object can serve as a placeholder. This placeholder object is called the “Proxy” for the complex object.
#include <iostream>
#include <memory>
class ICar {
public:
virtual ~ICar() { std::cout << "ICar destructor!" << std::endl; }
virtual void DriveCar() = 0;
};
class Car : public ICar {
public:
void DriveCar() override { std::cout << "Car has been driven!" << std::endl; }
};
class ProxyCar : public ICar {
public:
ProxyCar(int driver_age) : driver_age_(driver_age) {}
void DriveCar() override {
if (driver_age_ > 16) {
real_car_->DriveCar();
} else {
std::cout << "Sorry, the driver is too young to drive." << std::endl;
}
}
private:
std::unique_ptr<ICar> real_car_ = std::make_unique<Car>();
int driver_age_;
};
int main() {
std::unique_ptr<ICar> car = std::make_unique<ProxyCar>(16);
car->DriveCar();
car = std::make_unique<ProxyCar>(25);
car->DriveCar();
return 0;
}
Curiously Recurring Template
[edit | edit source]This technique is known more widely as a Mixin. Mixins are described in the literature to be a powerful tool for expressing abstractions[citation needed].
Interface-based Programming (IBP)
[edit | edit source]Interface-based programming is closely related with Modular Programming and Object-Oriented Programming, it defines the application as a collection of inter-coupled modules (interconnected and which plug into each other via interface). Modules can be unplugged, replaced, or upgraded, without the need of compromising the contents of other modules.
The total system complexity is greatly reduced. Interface Based Programming adds more to modular Programming in that it insists that Interfaces are to be added to these modules. The entire system is thus viewed as Components and the interfaces that helps them to co-act.
Interface-based Programming increases the modularity of the application and hence its maintainability at a later development cycles, especially when each module must be developed by different teams. It is a well-known methodology that has been around for a long time and it is a core technology behind frameworks such as CORBA. [citation needed]
This is particularly convenient when third parties develop additional components for the established system. They just have to develop components that satisfy the interface specified 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".