The Singleton: The Alternatives Monostate Pattern and Dependency Injection

Contents[Show]

So far, I have discussed in my previous posts the Singleton Pattern, and its pros and cons. One question is still open: What alternatives for the Singleton Pattern are available? Today, I write about the Monostate Pattern and Dependency Injection.

 CreationalPatterns

I want to start this post with the Monostate Pattern.

The Monostate Pattern

The Monostate Pattern is similar to the Singleton Pattern and quite popular in Python. While the Singleton Pattern guarantees that only one instance of a class exists, the Monostate Pattern ensures that all instances of a class share the same state. The Monostate Pattern is also known as Borgidiom, because the Borgs in the Star Trek series share a common memory.

In the Monostate Pattern, all data members are static. Consequentially, all instances of the class use the same data. The member function to access the data are non-static. Users of the instances are unaware of the singleton-like behavior of the class.

// monostate.cpp

#include <iostream>
#include <string>
#include <unordered_map>

class Monostate {
  
 public:
    
    void addNumber(const std::string& na, int numb) {
        teleBook[na] = numb;
    }
 
    void getEntries () const {
        for (auto ent: teleBook){ 
            std::cout << ent.first << ": " << ent.second << '\n';
        }
    }
    
private:
    static std::unordered_map<std::string, int> teleBook;
 
};

std::unordered_map<std::string, int> Monostate::teleBook{};

int main() {
    
    std::cout << '\n';
    
    Monostate tele1;
    Monostate tele2;
    tele1.addNumber("grimm", 123);
    tele2.addNumber("huber", 456);
    tele1.addNumber("smith", 789);
    
    tele1.getEntries();
    
    std::cout << '\n';
    
    tele2.getEntries();
    
    std::cout << '\n';
    
}

 

Each instance of the class Monostate shares the same state:

monostate

 

You probably think about Dependency Injection in the first place when you search for the alternative for the Singleton Pattern.

 

Rainer D 6 P2 540x540Modernes C++ Mentoring

Stay informed about my mentoring programs. Subscribe for the news.

 

 

Dependency Injection

The Singleton Pattern has serious drawbacks that I described in the previous post "The Singleton: Pros and Cons", Consequentially, the Singleton Pattern would probably not be part of a reprint of the book "Design Patterns: Elements of Reusable Object-Oriented Software". Instead, Dependency Injection is a highly likely future candidate.

The key idea of Dependency Injection is that an object or function (client) receives the other service it depends on. Therefore, the client is not aware of the construction of the service. The client is fully separated from the service that is injected by an injector. This is in contrast to the Singleton Pattern, where the client creates the service if needed.

int client(){
  
  ...

  auto singleton = Singleton::getInstance();
  singleton.doSomething();

  ...

}

 

Dependency Injection is a form of inversion of control. Not the client creates and calls the service, but the injector injects the service into the client.

In C++, three types of Dependency Injection are typically used:

  • Constructor injection
  • Setter injection
  • Template parameter injection

I use constructor injection and setter injection in the following program dependencyInjection.cpp.

// dependencyInjection.cpp

#include <chrono>
#include <iostream>
#include <memory>

class Logger {
public:
    virtual void write(const std::string&) const = 0;
    virtual ~Logger() = default;
};

class SimpleLogger: public Logger {
    void write(const std::string& mess) const override {
        std::cout << mess << '\n';
    }
};

class TimeLogger: public Logger {
    typedef std::chrono::duration<long double> MySecondTick;
    long double timeSinceEpoch() const {
        auto timeNow = std::chrono::system_clock::now();
        auto duration = timeNow.time_since_epoch();
        MySecondTick sec(duration);
        return sec.count();
    }
    void write(const std::string& mess) const override {
        std::cout << std::fixed;
        std::cout << "Time since epoch: " << timeSinceEpoch() << ": " << mess << '\n';
    }

};

class Client {
public:
    Client(std::shared_ptr<Logger> log): logger(log) {}   // (1)
    void doSomething() {
        logger->write("Message");
    }
    void setLogger(std::shared_ptr<Logger> log) {         // (2)
        logger = log;
    }
private:
    std::shared_ptr<Logger> logger;
};
        

int main() {
    
    std::cout << '\n';
    
    Client cl(std::make_shared<SimpleLogger>());
    cl.doSomething();
    cl.setLogger(std::make_shared<TimeLogger>());
    cl.doSomething();
    cl.doSomething();
    
    std::cout << '\n';
 
}

 

The client cl requires logger functionality. First, the logger SimpleLogger is injected using the constructor (line 1), afterwards the logger is replaced with the more powerful logger TimeLogger. The setter member function allows it to inject the new logger. The client is fully decoupled from the logger. It simply supports the interfaces to inject loggers. 

Here is the output of the program.

 

dependencyInjection

There are many examples of Dependency Injection using template parameters in the Standard Template Library. I only name here a few of the containers.

  • The containers of the STL use a default allocator. This one can be replaced with a custom allocator.
  • The ordered associative container use std::less as the sorting criteria. Of course, you can replace it with another sorting criterion.
  • The unordered associative containers require a hash function and an equal function. Both are template parameters and can, therefore, be replaced.

Finally, let me write a few words about the three remaining creational Design Patterns.

The Three Remaining Creational Design Patterns

Abstract Factory

Abstract Factory lets you produce families of related objects without specifying their concrete classes. A typical example could be an IDE theme that consists of many related objects. For example, each IDE theme has different widgets, such as checkboxes, sliders, push buttons, and radio buttons. Typically, a concrete IDE theme has different factory methods for the various checkboxes, sliders, push buttons, and radio buttons .. .  A client could change the IDE theme and, therefore, the widgets during the use of the IDE.

Builder

Builder construct complex objects step by step. Thanks to the builder pattern, you can produce different types and representations of an object using the same stepwise construction process. The builder extracts the object construction code out of its class and moves it to separate objects called builders. Not all steps of the construction process must be called, and a step can have more than one builder.

Prototype

Prototype creates objects by cloning an existing object. The program factoryMethodWindowSlicingFixed.cpp in my previous post "The Factory Method (Slicing and Ownership Semantics)" is a prototype. The Prototype Pattern is similar to the factory method, but emphasizes the created prototypes' initialization. The factory method creates different objects by subclassing them. 

What's Next?

My next posts about Design Patterns are dedicated to the structural pattern. I will start with the adaptor pattern, which can be implemented in two ways: multiple inheritance or delegation.

 

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Evangelos Denaxas, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, and Phillip Diekmann.

 

Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, Ralf Abramowitsch, John Nebel, Mipko, and Alicja Kaminska.

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

New

Contact Me

Modernes C++,

RainerGrimmDunkelBlauSmall

Mentoring

Stay Informed about my Mentoring

 

English Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Interactive Course: The All-in-One Guide to C++20

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 6270

Yesterday 6484

Week 12754

Month 33501

All 10954962

Currently are 131 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments