7 design patterns that every developer should know
As a developer, you are constantly resolving problems. Many of these problems probably were already solved by other developers, so, why do we need to solve them again?
You have a problem, specifically a common problem, so, you try to find out if anyone already did the job solving, before you try to resolve it, right? You probably don’t want to reinvent the wheel, do ya? Design patterns are there for these situations. Unfortunately, there are a lot of developers who still refuse to use a few patterns, mostly because they just don’t know them or even don’t know how to fit those patterns into some problems.
The main question that every developer ask before using design patterns is: are design patterns really that important? Someone could make a big explanation about this, but, I’ll be succinct: yes, they are. And I’m going to list three main reasons:
It saves your time. You don’t need to use your brain to recreate a possible solution from nowhere and lose lots of time;
Design patterns are well-known. When you say: “I used a factory to create that object” everyone will understand what you are talking about;
Most of these patterns are easy to understand. Probably your solution will be not as elegant and easy as a design pattern.
Okay, so I’m going to talk about the most useful design patterns and explain in which situations you should use them. :)
Before continuing, let’s be clear: there is no silver bullet. You need to adapt the design pattern to your problem and not try to adapt your problem to the design pattern. If you need to do it, probably it is not the right design pattern for this problem.
Well, here we go!
SINGLETON
This is the most used pattern. A lot of frameworks already implement this pattern, such as Spring, EJBs (using @Singleton).
There is no secret. You need an object that only needs to be instantiated once, so, you can use a singleton. The class needs to declare a private constructor to prevent people to instantiate it from outside the class. Also, you need to declare a static field of the type of the singleton. The method getInstance() assures that only one instance of this class is created at runtime.
#include <iostream>
using namespace std;class SingletonSample
{
public:
static SingletonSample& getInstance();
private:
SingletonSample () {std::cout << "Ctor\n";};
~SingletonSample () {std::cout << "Dtor\n";};
SingletonSample (const SingletonSample &);
const SingletonSample & operator=(const Singleton&);
};
SingletonSample& SingletonSample ::getInstance()
{
static SingletonSample instance;
return instance;
}
int main()
{
SingletonSample &s1; = SingletonSample::getInstance();
SingletonSample &s2; = SingletonSample::getInstance();
return 0;
}
But this does not thread safe because there could be a race condition during the initialization of the static Singleton,
SingletonSample& SingletonSample::getInstance(). But we can make the method thread safe by adding a mutex lock:
SingletonSample& SingletonSample ::getInstance()
{
static SingletonSample instance;
return instance;
}Notes
In 2009, the authors of the original design patterns said the only pattern they would consider removing from the original list is Singleton. This is because it is essentially a way to store global data and tends to be an indicator of poor design. There are several alternatives to the Singleton pattern: Dependency Injection, Monostate Pattern, Session Context
THE STRATEGY AND THE FACTORY PATTERN
Both are well-known design patterns. For sure two of the most useful design patterns, especially using them together.
When both are combined, you can create objects from a given qualifier. The example is right below:
#include <iostream>
#include <string>
using namespace std;class Building
{
public:
virtual string getType() = 0;
};class House:public Building
{
public:
string getType(){
return "House Type";
}
};class Office:public Building
{
public:
string getType(){
return "Office Type";
}
};class BuildingFactory
{
public:
Building* getBuilding(string type){
if(type == "House"){
return new House();
}
else if(type == "Office"){
return new Office();
}
}
};int main()
{
BuildingFactory* buildingFactory = new BuildingFactory();
Building* building = buildingFactory->getBuilding("House");
cout<<"Type is: " << building->getType() << endl;
building = buildingFactory->getBuilding("Office");
cout<<"Type is: " << building->getType() << endl;
}
If you need a specific building, you need just to give a building type and one will be returned, or null there is no instance for this type. Very useful, besides, it gives us a chance to use the most of polymorphism.
FLUENT BUILDER
Some objects require lots of parameters to be created and creating it becomes a pain. In this case, either using the constructor to create this object or using the setters will make our code ugly and hard to understand.
#include <iostream>
using namespace std;
/* Interface that will be returned as the product from builder */
class HousePlan{
public:
virtual void setWindow(string window)=0;
virtual void setDoor(string door)=0;
virtual void setBathroom(string bathroom)=0;
virtual void setKitchen(string kitchen)=0;
virtual void setFloor(string floor)=0;
};
/* Concrete class for the HousePlan interface */
class House:public HousePlan{
private :
string window, door, kitchen, bathroom, floor;
public:
void setWindow(string window){
this->window = window;
}
void setDoor(string door){
this->door = door;
}
void setBathroom(string bathroom){
this->bathroom = bathroom;
}
void setKitchen(string kitchen){
this->kitchen = kitchen;
}
void setFloor(string floor){
this->floor = floor;
}
};
/* Builder Class */
class HouseBuilder
{
public:
/* Abstract functions to build parts */
virtual void buildWindow()=0;
virtual void buildDoor()=0;
virtual void buildKitchen()=0;
virtual void buildBathroom()=0;
virtual void buildFloor()=0;
/* The product is returned by this function */
virtual House* getHouse()=0;
};
/* Concrete class for the builder interface */
class LavishHouse:public HouseBuilder
{
private:
House *house;
public:
LavishHouse(){
house = new House();
}
void buildWindow(){
house->setWindow("French Window");
}
void buildDoor(){
house->setDoor("Wooden Door");
}
void buildBathroom(){
house->setBathroom("Modern Bathroom");
}
void buildKitchen(){ {
house->setKitchen("Modular Kitchen");
}
void buildFloor(){
house->setFloor("Wooden Floor");
}
House* getHouse(){
return this->house;
}
};
/* Another Concrete class for the builder interface */
class NormalHouse:public HouseBuilder
{
private:
House *house;
public:
NormalHouse(){
house = new House();
}
void buildWindow(){
house->setWindow("Normal Window");
}
void buildDoor(){
house->setDoor("Metal Door");
}
void buildBathroom(){
house->setBathroom("Regular Bathroom");
}
void buildKitchen(){
house->setKitchen("Regular Kitchen");
}
void buildFloor(){
house->setFloor("Mosaic Floor");
}
House* getHouse(){
return this->house;
}
};
/* The Director. Constructs the house */
class Contractor
{
private:
HouseBuilder *houseBuilder;
public:
Contractor(HouseBuilder *houseBuilder){
this->houseBuilder = houseBuilder;
}
House *getHouse(){
return houseBuilder->getHouse();
}
void buildHouse(){
houseBuilder->buildWindow();
houseBuilder->buildDoor();
houseBuilder->buildBathroom();
houseBuilder->buildKitchen();
houseBuilder->buildFloor();
}
};
/* Example on how to use the Builder design pattern */
int main()
{
HouseBuilder *lavishHouseBldr = new LavishHouse();
HouseBuilder *normalHouseBldr = new NormalHouse();
Contractor *ctr1 = new Contractor(lavishHouseBldr);
Contractor *ctr2 = new Contractor(normalHouseBldr);
ctr1->buildHouse();
House *house1 = ctr1->getHouse();
cout<<"Constructed: "<<house1;
ctr2->buildHouse();
House *house2 = ctr2->getHouse();
cout<<"Constructed: "<<house2;
}CHAIN OF RESPONSIBILITY
You always need to build applications which require a lot of business logic. Behind this much of logic, there is always high complexity. This high complexity makes our code harder to understand, as well as harder to track, to log and so on.
The CoR pattern makes us break our code into little pieces and organize them into sequential steps.
#include <iostream>
using namespace std;
// Abstract class called Handler
// It is abstract because it has a pure virtual function.
// This prevents instances of Handler being created directly.
class Handler {
protected:
Handler *next;
public:
// Constructor
Handler() { next = NULL; }
// Pure virtual function
virtual void request(int value) = 0;
// Sets next handler in the chain
void setNextHandler(Handler *nextInLine) {
next = nextInLine;
}
};
// SpecialHandler is a type of Handler but has a limit and ID
// It also determines if it can handle the request or needs to send it on
// If it is the last in the chain and can't handle it, it lets the user know.
class specialHandler : public Handler {
private:
int limit;
int ID;
public:
specialHandler(int theLimit, int theID) {
limit = theLimit;
ID = theID;
}
// Handles incoming request
void request(int value) {
if (value < limit) {
cout<<"Handler "<<ID<<" limit "<<limit<<endl;
}
else if (next != NULL) {
next->request(value);
}
else {
cout << "Sorry, I am the last handler (" << ID << ") and I couldn't even handle that request." << endl;
}
}
};
int main ()
{
// Create three special handlers with ids "1, 2 and 3"
Handler *h1 = new specialHandler(10, 1);
Handler *h2 = new specialHandler(20, 2);
Handler *h3 = new specialHandler(30, 3);
// Chain up the handlers together
h1->setNextHandler(h2);
h2->setNextHandler(h3);
// Handled by handler 2 because handler 1 couldn't handle it,
// so it passed it on to handler 2 which could handle since it is less than 20
h1->request(18);
// No handler could handle this, so will trigger last handler's else
// statement showing that it is the last and still couldn't handle the request.
// You could also tack on a default version for the end if you like.
h1->request(40);
// Clean up our pointers
delete h1;
delete h2;
delete h3;
return 0;
}Now we can break our code into Commands and separate each logic in just one place. Also, we can reorganize as we want, making our code more decoupled, thus solving our complexity problem.
TEMPLATE METHOD
This pattern defines a skeleton in a method for a operation. It is very useful when you have common method calls but different behaviors. This pattern is totally based on polymorphism.
#include <iostream>
using namespace std;
class AbstractClass
{
public:
void templateMethod() {
primitiveOperation1();
primitiveOperation2();
concreteOperation();
hook();
}
virtual void primitiveOperation1() = 0;
virtual void primitiveOperation2() = 0;
void concreteOperation() {
cout << "Mandatory Operations for all ConcreteClasses" << endl;
}
virtual void hook() {}
};
class ConcreteClassA : public AbstractClass
{
public:
void primitiveOperation1() {
cout << "primitiveOp1 A" << endl;
}
void primitiveOperation2() {
cout << "primitiveOp2 A" << endl;
}
};
class ConcreteClassB : public AbstractClass
{
public:
void primitiveOperation1() {
cout << "primitiveOp1 B" << endl;
}
void primitiveOperation2() {
cout << "primitiveOp2 B" << endl;
}
void hook() {
cout << "hook() B" << endl;
}
};
int main()
{
ConcreteClassA ca;
ConcreteClassB cb;
ca.templateMethod();
cb.templateMethod();
return 0;
}STATE PATTERN
A lot of objects have states. For example, a radio. A radio has basically two states: on and off. Can we represent this in object-oriented programming?
#include<iostream>
using namespace std;
class Radio;
class RadioState
{
public:
virtual void execute(Radio *radio) = 0;
};
class Radio{
private:
bool on;
RadioState *state;
public:
Radio(RadioState *state){
this->state = state;
}
void execute(){
state->execute(this);
}
void setState(RadioState *state){
this->state = state;
}
void setOn(bool on){
this->on = on;
}
bool isOn(){
return on;
}
bool isOff(){
return !on;
}
};class OnRadioState : public RadioState
{
public:
virtual void execute(Radio *radio){
radio->setOn(true);
}
};
class OffRadioState : public RadioState
{
public:
virtual void execute(Radio *radio){
radio->setOn(false);
}
};
int main()
{
Radio* radio = new Radio(new OffRadioState());/
radio->setState(new OnRadioState());
radio->execute(); //radio on
radio->setState(new OffRadioState());
radio->execute(); //radio off
return 0;
}
This is just a simple example, but, when we talk about an object that has a lot of states, this control can help a lot. You can define rules to create final states and states that require a previous state to be executed. For example, in this radio scenario, you can only turn a radio off if the radio is on, otherwise, an exception will be thrown. Basically, you can do whatever the business tell you to do.
Follow me to stay up-to-date on this and other article series.
