Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C++

Templates for Design Patterns

Rate me:
Please Sign up or sign in to vote.
2.47/5 (6 votes)
31 Jan 2006CPOL4 min read 54.2K   131   28   13
A fly weight implementation of a C++ template library for applying Design Patterns easily in any application.

Introduction

Any Computer Science student at any university will, at some point, be faced with Design Patterns. Design Patterns are generic principles or best practices in software development. This article is not an introduction to Design Patterns, since others have already covered that in longer and better written articles. Instead, this article deals with implementing and re-using C++ templates when thinking Design Patterns.

Why use templates for Design Patterns?

The basic idea is to split up in functionality and interaction. By this, I mean we are able to implement a fully working class, e.g., a TCP/IP class, and at some point decide to use it as a singleton in one application and through a factory in another application. Using templates, the same base class can be used for either application without writing derivates and only adding a one-liner.

Singleton

Singleton is the most primitive Design Pattern ever made. The Singleton Design Patterns is a buzz-word for a global variable, a variable reachable from all other code. In C++, we could write the following code to generate a simple singleton:

C++
class my_singleton
{
private:
 std::string my_string;
public:
 my_singleton() : my_string("foo bar bash") {}

 static my_singleton &instance()
 {
  static my_singleton global_instance;
  return global_instance;
 }
 
 std::string get_string() { return my_string; }
};

Calling the global singleton to obtain access to the associated string is done as follows:

C++
...
cout << my_singleton::get_instance().get_string() << endl;

This is fairly simple and straightforward to implement and use. What happens when you want to refactor your code, or by some chance you learn that singleton is not the solution to all your prayers? Or, what if the code you work with to make a singleton is third party? Using the templated design pattern library, you are able to refactor code and change the design without having to worry about this. It's quite easy to create a singleton and use it, just watch and learn:

C++
class third_party_code
{
public:
 third_party_code();
 bool execute_heavy_code();
};
typedef template_pattern::singleton<third_party_code> global_third_party_code;

In the above snippet, the class third_party_code is supplied by an external developer, and we do not have access or rights to alter the code. Instead, we declare a type name global_third_party_code which is an alias for our singleton instance globalizing the third_party_code instance. Using the singleton is done as follows:

C++
...
if ((*global_third_party_code()).execute_heavy_code())
{
 ...
}

The library supplied with this article supports a simple implementation of the Singleton pattern, and does not handle the multitude of issues that may arise as a side effect of the Singleton pattern.

Implementing the singleton in the primitive case, i.e., with no mutex guarding against multi-threading, is shown in this code snippet:

C++
...
template <class T><CLASS T> class singleton
{
  public:
    T & instance()
    {
      static T object;
      return object;
    }
    T & operator* ()
    {
      return instance();
    }
};
...

As for whiter the singleton Design Patterns is a good design choice or not is left for others to decide. However, it may come in handy in some cases. A weapon to kill singletons is injection.

Factory

In contrast to the Singleton Design Pattern, we now take a look at the Dactory Design Pattern. The basic idea of the Factory Design Pattern is to have a central repository for instantiating objects implementing a given interface. Having the instantiation in a central location makes it easy to swap components, e.g., change the real database object with a stub object that doesn't persist information in the database. A straightforward way of using this Factory Design Pattern is shown below:

C++
class data_source
{
public:
 virtual std::vector<std::string> get_string_rows(std::string query) = 0;
 virtual void put_string_rows(const std::vector<std::string> data, 
                              const std::string query) = 0;
};

class database_data_source
{
public:
 database_data_source() { (*db_singleton()).prepare(); }
 virtual std::vector<std::string> get_string_rows(std::string query) { ... }
 virtual void put_string_rows(const std::vector<std::string> data, 
                              const std::string query) { ... };
};

class data_source_factory
{
public:
 static data_source * create()
 {
  return new database_data_source;
 }
};

void do_stuff()
{
 data_source * ds = data_source_factory::create();
 ...
 delete ds;
}

Obviously, this is nice and very easy to read. Changing this to some more generic code, using the Design Pattern template library, we get the following code, which is shorter and easier to read:

C++
class data_source
{
public:
 virtual std::vector<std::string> get_string_rows(std::string query) = 0;
 virtual void put_string_rows(const std::vector<std::string> data, 
                              const std::string query) = 0;
};

class database_data_source
{
public:
 database_data_source() { (*db_singleton()).prepare(); }
 virtual std::vector<std::string> get_string_rows(std::string query) { ... }
 virtual void put_string_rows(const std::vector<std::string> data, 
                              const std::string query) { ... };
};

typedef template_patterns::factory<data_source, 
        database_data_source> data_source_factory;

void do_stuff()
{
 data_source * ds = data_source_factory::create();
 ...
 delete ds;
}

Again, the advantage is the flexibility within the code. Using templates, we no longer need to worry about maintaining the factory classes, and additionally, the templates can be reused over and over without duplicating code.

The basic factory template provided supports instantiation of new objects; in some cases, it is useful to initialize the newly instantiated object. This behaviour can be achieved through the factory_with_initializer template, as demonstrated below:

C++
class string_initializer
{
public:
  std::string * operator() (std::string *input)
  {
    (*input) = "I have been initialized";
    return input;
  }
};

typedef template_patterns::factory_with_initializer<std::string, std::string, 
                           string_initializer> string_factory;

void do_stuff()
{
 std::string * str = string_factory::create();
 cout << (*str) << endl;
 delete str;
}

The class string_initializer is parsed to the definition of the string factory; when the create function of the factory is called, it will automatically call the initializer function. The output of the do_stuff-function is "I have been initialized".

Mixing patterns

Say, what if you redesign the application and find that it would be nice if the factory was a singleton instance? No problem: having Design Patterns as templates makes it easy to change code design. Extending the data source example from above, we end up with the following code:

C++
typedef template_patterns::factory<data_source, database_data_source> data_source_factory;
typedef template_patters::singleton<data_source_factory> data_source_singleton_factory;
...
void do_stuff()
{
 data_source * ds = (*data_source_singleton_factory()).create();
 ...
 delete ds;
}

In the end

So, this concludes my tiny introduction to the template Design Pattern library. I have attached version 0.1 of the library, which will be enhanced when time is right. The most recent version of the library is available from my code-blog page found here:

Thanks for reading.

History

  • 01-02-06: Second edition. Library version 0.2. Added factory_with_initializer text.
  • 31-01-06: First edition. Library version 0.1.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Denmark Denmark
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHave you tried... ? Pin
Chulips14-Sep-06 9:21
Chulips14-Sep-06 9:21 
AnswerRe: Have you tried... ? Pin
Rasmus Kaae21-Sep-06 21:38
Rasmus Kaae21-Sep-06 21:38 
GeneralLoki & Boost might be a better choice Pin
Miles Davies1-Feb-06 3:26
Miles Davies1-Feb-06 3:26 
GeneralRe: Loki & Boost might be a better choice Pin
Rasmus Kaae1-Feb-06 6:52
Rasmus Kaae1-Feb-06 6:52 
GeneralRe: Loki & Boost might be a better choice Pin
Paulius Maruka5-Feb-06 6:12
Paulius Maruka5-Feb-06 6:12 
GeneralRe: Loki & Boost might be a better choice Pin
Rasmus Kaae5-Feb-06 19:52
Rasmus Kaae5-Feb-06 19:52 
GeneralComment Pin
WeaponX200631-Jan-06 7:09
WeaponX200631-Jan-06 7:09 
QuestionI don't get it? Pin
Jim Crafton31-Jan-06 3:45
Jim Crafton31-Jan-06 3:45 
AnswerRe: I don't get it? Pin
Rasmus Kaae31-Jan-06 6:42
Rasmus Kaae31-Jan-06 6:42 
GeneralRe: I don't get it? Pin
Rasmus Kaae31-Jan-06 6:43
Rasmus Kaae31-Jan-06 6:43 
Add <tcpip> to the factory and singleton typedefs.

---
Rasmus Kaae
http://www.3kings.dk
http://kalchas.dk
http://www.hestebasen.com
GeneralRe: I don't get it? Pin
Jim Crafton31-Jan-06 6:45
Jim Crafton31-Jan-06 6:45 
GeneralRe: I don't get it? Pin
Rasmus Kaae31-Jan-06 6:51
Rasmus Kaae31-Jan-06 6:51 
GeneralRe: I don't get it? Pin
Jim Crafton31-Jan-06 7:03
Jim Crafton31-Jan-06 7:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.