Sunday, 22 November 2015

Design Patterns - Strategy Pattern in C++

Design Patterns - Strategy Pattern in C++:

Introduction:
This pattern is used to encapsulate each one as an object, and make them interchangeable. The strategy pattern lets the algorithms vary independently from clients that use them.




#include <QtGui/QApplication>

#include <iostream>

#include <string>

using namespace std;

class SortingTech

{

    public:

        virtual void sort() const = 0;

};

class MergeSort: public SortingTech

{

    public:

        virtual void sort() const {

    qDebug ("I am in Merge sort Function\n");

        }

};

class QuickSort: public SortingTech {

    public:

        virtual void sort() const {

    qDebug ("I am in Quick sort Function\n");

        }

};

class HeapSort: public SortingTech

{

    public:

        virtual void sort() const {

    qDebug ( "I am in Heap sort Function\n");

        }

};

class Searching

{

    public:

        virtual void search() const = 0;

};

class Sequential: public Searching

{

    public:

        virtual void search() const {

    qDebug ("This is Sequential search function\n");

        }

};

class BinaryTree: public SearchingTech

{

    public:

        virtual void search() const {

    qDebug  ("This is BinaryTree search Function\n");

        }

};

class HashTable: public SearchingTech

{

    public:

        virtual void search() const {

            qDebug ("This is HashTable search()\n");

        }

};

// Context

class Collection

{

    private:

        SortingTech* m_sort;

        SearchingTech* m_search;

    public:

        Collection(){}

        void set_sort(SortingTech* s){

            m_sort = s;

        }

        void set_search(SearchingTech* s){

            m_search = s;

        }

        void sort() const {

            m_sort->sort();

        }

        void search() const {

            m_search->search();

        }

};

int main (int argc, char *argv[])

{

   QApplication a(argc, argv);

   MergeSort  ocMerge;

   QuickSort ocQuick;

   HeapSort ocHeap;

   Sequential ocSequential;

   BinaryTree ocBinaryTree;

   HashTable ocHashTable;

   Collection colA;

   colA.set_sort(&ocMerge);

   colA.sort();

   Collection colB;

   colB.set_search(&ocBinaryTree);

   colB.search();

   return a.exec ();

}


The output of the above program is
Starting C:\QtSDK\QtCreator\bin\Test\debug\Test.exe...

C:\QtSDK\QtCreator\bin\Test\debug\Test.exe exited with code 1

Starting C:\QtSDK\QtCreator\bin\Test\debug\Test.exe...

I am in Merge sort Function

This is BinaryTree search()


In the above program  we have two interfaces for Sorting  and SearchingTech along with corresponding classes that implement each concrete behavior.

With the design, other types of objects can reuse our search and sort behaviors because these behaviors are no longer hidden away in our Collection classes.

And we can add new behaviors without modifying any of our existing behavior classes.
We put instance variables hold a pointer to a specific behavior at runtime.
class Collection {

    private:

        SortTech* m_sort;

        SearchingTech* m_search;


Using the pointers, we can implement the each behaviour
        void sort() const {

            m_sort->sort();

        }

   void search() const {

            m_search->search();

        }

Rather than handling the sort behavior itself, the Collection object delegates that behavior to the object pointed by m_sort.
colA.set_sort(&merge);

colA.sort();

========

Collection::m_sort->sort();

=======

virtual void sort() const{

    cout << "Merge sort()\n";

}


using the composition which is giving us a lot more flexibility. Not only does it let us encapsulate a family of algorithm into their own set of classes, but it also let us change behavior at runtime as long as the object we're composing with implements the correct behavior interface.
In this sample, we have two ways of recording contact information: stream & database
#include <QtGui/QApplication>

#include <iostream>

#include <string>

using namespace std;

class Data

{

public:

    virtual void start_Data() = 0;

    virtual void store_field(const string &name, const string &value) = 0;

    virtual void finish_Data() = 0;

    virtual ~Data() { }

};

struct ContactData

{

    string first_name, last_name, TelNo, MailID;

};

class ContactDataer

{

public:

    ContactDataer(Data *a) : m_Data(a)

    {

        assert(a != 0);

    }

    void store(const ContactData &data)

    {

        assert(m_Data != 0);

        m_Data->start_Data();

        m_Data->store_field("first name", data.first_name);

        m_Data->store_field("last name", data.last_name);

        m_Data->store_field("TelNo", data.TelNo);

        m_Data->store_field("MailID", data.MailID);

        m_Data->finish_Data();

    }

private:

    Data *m_Data;

};

class StreamData : public Data

{

public:

    StreamData(ostream &s, const string &Data_name = string())

        : m_ostream(s), m_Data_name(Data_name)

    { }

    void start_Data() { m_ostream << m_Data_name << "( "; }

    void store_field(const string &name, const string &value)

    {

        m_ostream << name << ": " << value << "; ";

    }

    void finish_Data() { m_ostream << ")" << endl; }

    void set_Data_name(const string &name) { m_Data_name = name; }

private:

    ostream &m_ostream;

    string m_Data_name;

};

class MySql {};

class DatabaseData : public Data

{

public:

    DatabaseData() : m_dbConnection(new MySql) {}

    void start_Data() { cout << "start transaction\n"; }

    void store_field(const string &name, const string &value)

    { cout << "insert into table\n"; }

    void finish_Data() { cout << "finish transaction\n"; }

private:

    MySql *m_dbConnection;

};

int main (int argc, char *argv[])

{

   QApplication a(argc, argv);

   ContactData data = {"Base", "Test", "123-897-123", "Test@email.com"};

   StreamData sData(std::cout);

   ContactDataer contact(&sData);

   contact.store(data);

   DatabaseData dbData;

   ContactDataer contact2(&dbData);

   contact2.store(data);

   return a.exec ();

}


The output of the above program is
( first name: Base; last name: Test; phone: 123-897-123; email: Test@email.com; )

start transaction

insert into table

insert into table

insert into table

insert into table

finish transaction

1 comment:

pavuluri santhi said...

Benefits in using Strategy Pattern
1. It is very easy to alter the application behavior without changing its architecture.
2. It is very easy to implement new algorithms complying with the same interface can be easily introduced.
3. It is possible to switch the application strategies at run-time.
4. Strategy enables the clients to choose the required algorithm, without using a "switch" statement or a series of "if-else" statements.
5. Data structures used for implementing the algorithm is completely encapsulated in Strategy classes. Therefore, the implementation of an algorithm can be changed without affecting the Context class.
6. Strategy Pattern can be used instead of sub-classing the Context class.
Drawbacks in using Strategy Pattern
1. The application and the developer must be aware of all the strategies to select the right one for the right situation.
2. Strategy and Context classes to be coupled tightly.
3. Here the interface places the vital role, because the Context and the Strategy classes normally communicate through the interface specified by the abstract Strategy base class.
4. Strategy pattern base class must expose interface for all the required behaviors, which some concrete Strategy classes might not implement.
5. The application configures the Context with the required Strategy object.
6. So, the application needs to create and maintain two objects in place of one.