Wednesday, October 26, 2016

Object Oriented Design Patterns

Understanding design patterns is crucial for software development as they provide proven solutions to common design problems. Below are some fundamental design patterns with code samples in C++.

Singleton Pattern:

The Singleton Pattern ensures a class has only one instance and provides a global point of access to it.

#include<iostream>
#include<memory>

using namespace std;

class Singleton {
public:
    static Singleton* Instance() {
        if (!mInstance)
            mInstance = new Singleton();
        return mInstance;
    }

    void Display() {
        cout << "This is singleton instance" << endl;
    }

    static void Destroy() {
        if (mInstance) {
            delete mInstance;
            mInstance = nullptr;
        }
    }

protected:
    Singleton() {
        cout << "Singleton constructor" << endl;
    }
    ~Singleton() {
        cout << "Singleton destructor" << endl;
    }

private:
    static Singleton* mInstance;
};

Singleton* Singleton::mInstance = nullptr;

int main() {
    Singleton* sgn = Singleton::Instance();
    sgn->Display();
    Singleton::Destroy();
    return 0;
}


Factory Pattern:

The Factory Pattern defines an interface for creating an object but lets subclasses alter the type of objects that will be created.

#include<iostream>

using namespace std;

class Product {
public:
    Product() {};
    virtual ~Product() {};
};

class ConcreteProduct : public Product {
public:
    ConcreteProduct() {
        cout << "Construct of ConcreteProduct" << endl;
    }
    virtual ~ConcreteProduct() {
        cout << "Destruct of ConcreteProduct" << endl;
    }
};

class Factory {
public:
    Factory() {};
    virtual ~Factory() {};
    virtual Product* CreateProduct() = 0;
};

class ConcreteFactory : public Factory {
public:
    ConcreteFactory() {
        cout << "Construct of ConcreteFactory" << endl;
    }
    virtual ~ConcreteFactory() {
        cout << "Destruct of ConcreteFactory" << endl;
    }
    virtual Product* CreateProduct() {
        return new ConcreteProduct();
    }
};

int main() {
    Factory* factory = new ConcreteFactory();
    Product* p = factory->CreateProduct();
    delete p;
    delete factory;
    return 0;
}



Prototype Pattern:

The Prototype Pattern is used to create a new object by copying an existing object, known as the prototype.

#include<iostream>
#include<string>

using namespace std;

class Prototype {
public:
    virtual ~Prototype() {};
    virtual Prototype* Clone() const = 0;

protected:
    Prototype() { cout << "Prototype constructor" << endl; }
};

class ConcretePrototype : public Prototype {
public:
    ConcretePrototype() {};
    ConcretePrototype(const ConcretePrototype& cp) {
        cout << "ConcretePrototype copy ..." << endl;
    }
    Prototype* Clone() const override {
        return new ConcretePrototype(*this);
    }
};

int main() {
    Prototype* p = new ConcretePrototype();
    cout << "Clone is called" << endl;
    Prototype* p1 = p->Clone();
    delete p;
    delete p1;
    return 0;
}


Builder Pattern:

The Builder Pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.

#include<string>
#include<iostream>

using namespace std;

class Product {
public:
    Product() {};
    ~Product() {};
    void ProductPart() {};
};

class Builder {
public:
    virtual ~Builder() {};
    virtual void BuildPartA(const string& buildPara) = 0;
    virtual void BuildPartB(const string& buildPara) = 0;
    virtual void BuildPartC(const string& buildPara) = 0;
    virtual Product* GetProduct() = 0;
protected:
    Builder() {};
};

class ConcreteBuilder : public Builder {
public:
    ConcreteBuilder() {};
    ~ConcreteBuilder() {};
    void BuildPartA(const string& buildPara) override {
        cout << "Build Part A ---" << buildPara << endl;
    }
    void BuildPartB(const string& buildPara) override {
        cout << "Build Part B ---" << buildPara << endl;
    }
    void BuildPartC(const string& buildPara) override {
        cout << "Build Part C ---" << buildPara << endl;
    }
    Product* GetProduct() override {
        BuildPartA("pre-defined");
        BuildPartB("pre-defined");
        BuildPartC("pre-defined");
        return new Product();
    }
};

class Director {
public:
    Director(Builder* bld) : mbld(bld) {}
    ~Director() {};
    void Construct() {
        mbld->BuildPartA("user-defined");
        mbld->BuildPartB("user-defined");
        mbld->BuildPartC("user-defined");
    }
private:
    Builder* mbld;
};

int main() {
    Director* d = new Director(new ConcreteBuilder());
    d->Construct();
    delete d;
    return 0;
}



Adapter Pattern:

The Adapter Pattern allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.

#include<iostream>

using namespace std;

class Target {
public:
    virtual void Request() {
        cout << "Target Request" << endl;
    }
    virtual ~Target() {}
};

class Adaptee {
public:
    void SpecificRequest() {
        cout << "Adaptee SpecificRequest" << endl;
    }
};

class Adapter : public Target {
public:
    Adapter(Adaptee* adaptee) : adaptee_(adaptee) {}
    void Request() override {
        adaptee_->SpecificRequest();
    }
private:
    Adaptee* adaptee_;
};

int main() {
    Adaptee* adaptee = new Adaptee();
    Target* target = new Adapter(adaptee);
    target->Request();
    delete target;
    delete adaptee;
    return 0;
}


Composite Pattern:

The Composite Pattern composes objects into tree structures to represent part-whole hierarchies. It allows clients to treat individual objects and compositions of objects uniformly.

#include<iostream>
#include<vector>
#include<string>

using namespace std;

class Component {
public:
    virtual void Operation() = 0;
    virtual void Add(Component* component) {}
    virtual void Remove(Component* component) {}
    virtual Component* GetChild(int index) { return nullptr; }
    virtual ~Component() {}
};

class Leaf : public Component {
public:
    void Operation() override {
        cout << "Leaf Operation" << endl;
    }
};

class Composite : public Component {
public:
    void Operation() override {
        for (auto component : children_) {
            component->Operation();
        }
    }

    void Add(Component* component) override {
        children_.push_back(component);
    }

    void Remove(Component* component) override {
        children_.erase(remove(children_.begin(), children_.end(), component), children_.end());
    }

    Component* GetChild(int index) override {
        if (index < 0 || index >= children_.size()) {
            return nullptr;
        }
        return children_[index];
    }

private:
    vector<Component*> children_;
};

int main() {
    Composite* root = new Composite();
    Leaf* leaf1 = new Leaf();
    Leaf* leaf2 = new Leaf();
    Composite* subtree = new Composite();

    root->Add(leaf1);
    root->Add(subtree);
    subtree->Add(leaf2);

    root->Operation();

    delete root;
    delete leaf1;
    delete leaf2;
    delete subtree;
    return 0;
}

Strategy Pattern: 

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. This pattern allows the algorithm to vary independently from clients that use it.

#include<iostream>

using namespace std;

class Strategy {
public:
    virtual void Execute() = 0;
    virtual ~Strategy() {}
};

class ConcreteStrategyA : public Strategy {
public:
    void Execute() override {
        cout << "Executing Strategy A" << endl;
    }
};

class ConcreteStrategyB : public Strategy {
public:
    void Execute() override {
        cout << "Executing Strategy B" << endl;
    }
};

class Context {
public:
    void SetStrategy(Strategy* strategy) {
        strategy_ = strategy;
    }
    void ExecuteStrategy() {
        if (strategy_) {
            strategy_->Execute();
        }
    }
private:
    Strategy* strategy_;
};

int main() {
    Context context;
    Strategy* strategyA = new ConcreteStrategyA();
    Strategy* strategyB = new ConcreteStrategyB();

    context.SetStrategy(strategyA);
    context.ExecuteStrategy();

    context.SetStrategy(strategyB);
    context.ExecuteStrategy();

    delete strategyA;
    delete strategyB;
    return 0;
}

Observer Pattern:

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

#include<iostream>
#include<vector>
#include<string>

using namespace std;

class Observer {
public:
    virtual void Update(const string& message) = 0;
    virtual ~Observer() {}
};

class Subject {
public:
    void Attach(Observer* observer) {
        observers_.push_back(observer);
    }

    void Detach(Observer* observer) {
        observers_.erase(remove(observers_.begin(), observers_.end(), observer), observers_.end());
    }

    void Notify(const string& message) {
        for (auto observer : observers_) {
            observer->Update(message);
        }
    }
private:
    vector<Observer*> observers_;
};

class ConcreteObserver : public Observer {
public:
    ConcreteObserver(const string& name) : name_(name) {}
    void Update(const string& message) override {
        cout << "Observer " << name_ << ": " << message << endl;
    }
private:
    string name_;
};

int main() {
    Subject subject;
    Observer* observer1 = new ConcreteObserver("Observer 1");
    Observer* observer2 = new ConcreteObserver("Observer 2");

    subject.Attach(observer1);
    subject.Attach(observer2);

    subject.Notify("Subject state has changed!");

    subject.Detach(observer1);
    subject.Notify("Subject state has changed again!");

    delete observer1;
    delete observer2;
    return 0;
}

Command Pattern:

The Command Pattern encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

#include<iostream>
#include<vector>
#include<string>

using namespace std;

class Command {
public:
    virtual void Execute() = 0;
    virtual ~Command() {}
};

class Receiver {
public:
    void Action() {
        cout << "Receiver Action" << endl;
    }
};

class ConcreteCommand : public Command {
public:
    ConcreteCommand(Receiver* receiver) : receiver_(receiver) {}
    void Execute() override {
        receiver_->Action();
    }
private:
    Receiver* receiver_;
};

class Invoker {
public:
    void SetCommand(Command* command) {
        command_ = command;
    }
    void ExecuteCommand() {
        if (command_) {
            command_->Execute();
        }
    }
private:
    Command* command_;
};

int main() {
    Receiver receiver;
    Command* command = new ConcreteCommand(&receiver);
    Invoker invoker;

    invoker.SetCommand(command);
    invoker.ExecuteCommand();

    delete command;
    return 0;
}


Decorator Pattern:

The Decorator Pattern allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.

#include<iostream>

using namespace std;

class Component {
public:
    virtual ~Component() {};
    virtual void Operation() = 0;
protected:
    Component() {};
};

class ConcreteComponent : public Component {
public:
    ConcreteComponent() {};
    void Operation() override {
        cout << "ConcreteComponent operation" << endl;
    }
    ~ConcreteComponent() {};
};

class Decorator : public Component {
public:
    Decorator(Component* com) : mCom(com) {};
    virtual ~Decorator() { delete mCom; };
    void Operation() override {};
protected:
    Component* mCom;
};

class ConcreteDecorator : public Decorator {
public:
    ConcreteDecorator(Component* com) : Decorator(com) {}
    ~ConcreteDecorator() {};
    void Operation() override {
        mCom->Operation();
        AddedBehavior();
    }
    void AddedBehavior() {
        cout << "ConcreteDecorator::AddedBehavior" << endl;
    }
};

int main() {
    Component* com = new ConcreteComponent();
    Decorator* dec = new ConcreteDecorator(com);
    dec->Operation();
    delete dec;
    return 0;
}

          

Chain of Responsibility Pattern: The Chain of Responsibility Pattern avoids coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. This pattern chains the receiving objects and passes the request along the chain until an object handles it.
#include<iostream>

using namespace std;

class Handler {
public:
    virtual void SetNextHandler(Handler* handler) {
        next_handler_ = handler;
    }
    virtual void HandleRequest(int request) {
        if (next_handler_) {
            next_handler_->HandleRequest(request);
        }
    }
    virtual ~Handler() {}
protected:
    Handler* next_handler_ = nullptr;
};

class ConcreteHandler1 : public Handler {
public:
    void HandleRequest(int request) override {
        if (request < 10) {
            cout << "ConcreteHandler1 handled request: " << request << endl;
        } else if (next_handler_) {
            next_handler_->HandleRequest(request);
        }
    }
};

class ConcreteHandler2 : public Handler {
public:
    void HandleRequest(int request) override {
        if (request >= 10 && request < 20) {
            cout << "ConcreteHandler2 handled request: " << request << endl;
        } else if (next_handler_) {
            next_handler_->HandleRequest(request);
        }
    }
};

class ConcreteHandler3 : public Handler {
public:
    void HandleRequest(int request) override {
        if (request >= 20) {
            cout << "ConcreteHandler3 handled request: " << request << endl;
        } else if (next_handler_) {
            next_handler_->HandleRequest(request);
        }
    }
};

int main() {
    Handler* handler1 = new ConcreteHandler1();
    Handler* handler2 = new ConcreteHandler2();
    Handler* handler3 = new ConcreteHandler3();

    handler1->SetNextHandler(handler2);
    handler2->SetNextHandler(handler3);

    int requests[] = {5, 14, 22, 18, 3, 27};

    for (int request : requests) {
        handler1->HandleRequest(request);
    }

    delete handler1;
    delete handler2;
    delete handler3;

    return 0;
}

Bridge Pattern:

The Bridge Pattern decouples an abstraction from its implementation so that the two can vary independently.
#include<iostream>

using namespace std;

class AbstractionImp {
public:
    virtual ~AbstractionImp() {};
    virtual void Operation() = 0;
protected:
    AbstractionImp() {};
};

class Abstraction {
public:
    virtual ~Abstraction() {};
    virtual void Operation() = 0;
protected:
    Abstraction() {};
};

class RefinedAbstraction : public Abstraction {
public:
    RefinedAbstraction(AbstractionImp* imp) : mImp(imp) {};
    ~RefinedAbstraction() {};
    void Operation() override {
        mImp->Operation();
    }
private:
    AbstractionImp* mImp;
};

class ConcreteAbstractionImpA : public AbstractionImp {
public:
    ConcreteAbstractionImpA() {};
    ~ConcreteAbstractionImpA() {};
    void Operation() override {
        cout << "ConcreteAbstractionImpA" << endl;
    }
};

int main() {
    AbstractionImp* imp = new ConcreteAbstractionImpA();
    Abstraction* abs = new RefinedAbstraction(imp);
    abs->Operation();
    delete abs;
    delete imp;
    return 0;
}


These patterns are essential building blocks in software design. They provide a common language and proven solutions to recurring design problems, enabling developers to create more robust, maintainable, and scalable software systems.

No comments:

Post a Comment