Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

An Implementation of the Service Locator Pattern in C++

4.20/5 (3 votes)
19 Feb 2008CPOL3 min read 1   469  
The Service Locator can be used to decouple classes, which improves the overall design and significantly helps with unit testing.

Introduction

This article assumes knowledge of the Service Locator pattern. The article uses terminology and source examples from Martin Fowler's Inversion of Control Containers and the Dependency Injection Pattern. Also see the CodeProject article A Basic Introduction on Service Locator Pattern.

Background

Typical examples are in C# and Java. This article provides a simple but complete Service Locator framework in C++.

The basic idea behind the Service Locator is to be able to register one or more classes with a repository and then ask the repository to create instances of those classes using an ID rather than the actual class name. By doing this, it makes the client code decoupled from the actual implementation of the class, allowing clients to register different implementations of the same interface and thus being able to swap different sets of functionality without changing the code that uses the class. This also helps with unit testing because references to a class that would ordinarily be hard-coded no longer needs to be.

And finally, this also reduces build dependencies.

Sneak peak at usage

In this article, Martin Fowler's example classes are used: MovieFinder is an interface (abstract pure-virtual class) and ColonDelimitedMovieFinder is a class which implements MovieFinder.

The Service Locator code will allow us to register ColonDelimitedMovieFinder with a string ID such as "finder", and then create instances of the ColonDelimitedMovieFinder classes by just using the string name "finder", assigning the referencing to a MovieFinder pointer.

Example registering the class, storing it under the string ID "finder":

C++
locator.register_class<ColonDelimitedMovieFinder>( "finder" );

Example creating an instance of this class:

MovieFinder* finder = locator.get_single_instance<MovieFinder>( "finder" );

Using the Code

(You just need the single header, servicelocator.h. The namespace sl is used.)

Derive your interfaces (pure-virtual abstract classes) from interface_t. Example:

struct MovieFinder : sl::interface_t
{
    virtual vector<Movie> findAll() = 0;
};

Derive your template classes from member_t, passing in the class name as the template parameter. Example:

class ColonDelimitedMovieFinder : public sl::member_t<ColonDelimitedMovieFinder>
{ ...

Create an instance of servicelocator_t. Example:

sl::servicelocator_t locator;

Register classes using the register_class method. The class to be registered is the template parameter. The function parameter is the string ID representing the class. Example:

locator.register_class<ColonDelimitedMovieFinder>( "finder" ); 

Now, the string ID (such as "finder") above can be created with an instance of ColonDelimitedMovieFinder, without referencing ColonDelimitedMovieFinder by name. Assuming that ColonDelimitedMovieFinder is redefined to implement the MovieFinder interface like so:

class ColonDelimitedMovieFinder : public sl::member_t<ColonDelimitedMovieFinder>, 
                                  public MovieFinder

Then, there are two methods for creating an instance of ColonDelimitedMovieFinder. One as a singleton (so that subsequent calls to create all return the same instance), and one as a transient instance, where a new instance is created with each call.

The call to create a singleton instance is get_single_instance, and it looks like this:

MovieFinder* finder = locator.get_single_instance<MovieFinder>( "finder" );

(Again, note that the class ColonDelimitedMovieFinder is not referenced, but that's what's created because the string ID "finder" was registered with ColonDelimitedMovieFinder above.) Note that the object returned from get_single_instance does not need to be freed. Only a single instance is created and is owned by the Service Locator. When the locator object goes out of scope, or is freed, all singletons are freed.

The second way to create an instance is via get_new_instance, and it looks like this:

auto_ptr<MovieFinder> finder = locator.get_new_instance<MovieFinder>( "finder" );

For convenience, get_new_instance returns an auto_ptr, which is safer to return than a raw pointer. However, if the client doesn't want an auto_ptr object, it's simple enough to call release() on the auto_ptr and get the raw object, which then can be managed by the client as desired. But in that case, the client is responsible for calling delete on the raw object eventually.

License

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