Last Updated: March 02, 2016
·
564
· themichael'tips

Everyday with Template Design Pattern

Introduction

Every programmer has a intrinsic programmation style. I'm not talking about code indentation or google code style guide.

I'm talking about the way people write code.

Let's begin with a real example: you have to write mongo/mysql/oracle queries, and you'll do the best you can. You will use the most sophisticated and cool stuff..and that's great! The problem arise when someone else must understand this original code that performs a not original operation.

A way to exprime creativity and create homogeneous and understandable codebase is the use of the Template Design Pattern. Here I describe a different point of view about this pattern, which is:

The Template Design Pattern allow to a group of programmers to express their creativity, staying in harmony with the codebase.

A real exemple

The below code represent the base class of the Template Design Pattern that allow a generic way to perform queries.

Note

  1. The C++ template keyword is not essential in construction of the the template design pattern. I use the C++ template to create an intelligent class that could deal with any kind of data.
  2. The examples are generic, pseudocode is used to describe operation related to a generic database driver (sql or not)
template<typename T>
class Query {
public:
    Query() {
        connection_ = GetCurrentThreadConnection();
    }

    /* Here the essence of the design pattern */
    T query() {    
      build();
      run();
      return extract();
    }

    ~Query() {}

protected:
    virtual void build() = 0;
    virtual void run() = 0;
    virtual T extract() = 0;

protected:
    ConnectionType connection_;
    ConnectionCursor cursor_;
    QueryBuild query_;
    bool operation_status_;
};

Keys of the base Query class are:

  • build(), run() and extract() are protected and virtual.
  • query() contain the workflow: how the different pieces of software are combined.
  • automatic connection to the current thread session is done once: subclass just use it.
  • extra: using the C++ template, programmers are free to play with any data.

Let's create some queries

Suppose we have to update some data. What I expect is a class/method that get as:

  • input -> my data
  • output -> true/false or successful/error code or throw exception

This simple task can be performed by several code. What would be nice is a way in which all those different code could be guided in order to be accessible by anybody.

A simple implementation is:

class UpdateData: public Query<bool> {
public:
    UpdateData(int data) : data_(data) {}

protected:
    virtual void build() {
       query_ = QueryBuild("set data = " << data_);
    }

    virtual void run() {
       operation_status_ = connection_->update(query_);
    }

    virtual bool extract() {
        if (connection_->get_last_error() == true)
            return false;

        return operation_status_;
    }

private:
    int data_;
};

The three steps - that make up a common query - are well isolated.<br>
It's easy to explore the code (here are just few lines, but suppose big and more complex queries), you know where looking for some specific parts of it.<br>
Your creativity and your experience, like advanced techniques for dealing with memory, exceptions, etc, can be exploited here, without damaging your skills.

Let's explore a new example. How the code should look like, if we have to retrieve information from database ?

/* Define custom type */
typedef struct __data__ {
  int field_1;
  std::string field_2;
} data;

typedef std::list<data> data_list;

/* this class return a data_list element */
class GetData: public Query<data_list> {
public:
    GetData(int condition) : condition_(condition) {}

protected:
    virtual void build() {
       query_ = QueryBuild("where data = " << condition_);
    }

    virtual void run() {
       cursor_ = connection_->query(query_);
    }

    virtual data_list extract() {
        data_list mydata;

        if (connection_->get_last_error() == true) {
            throw my_exception;
        }

        while (cursor_->more()) {
            data d;
            auto obj = cursor_->next();
            d.field_1 = obj["field_1"].toInt();
            d.field_2 = obj["field_2"].toString();
            mydata.push_back(std::move(d));
        }

        return mydata;
    }

private:
    int condition_;
};

Conclusion

Just a glance and the code reveals its secret, how is subdivided and structured: you known where to look.<br>
The Template Design Pattern give the guidelines - you give the creativity.