CreationalPatterns

The Singleton: The Alternatives Monostate Pattern and Dependency Injection

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.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

Be part of my mentoring programs:

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (starts March 2024)
  • Do you want to stay informed: Subscribe.

     

    // 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.

    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, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, 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, 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, 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, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, moon, Philipp Lenk, Hobsbawm, and Charles-Jianye Chen.

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

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Seminars

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

    Standard Seminars (English/German)

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

    • C++ – The Core Language
    • C++ – The Standard Library
    • C++ – Compact
    • C++11 and C++14
    • Concurrency with Modern C++
    • Design Pattern and Architectural Pattern with C++
    • Embedded Programming with Modern C++
    • Generic Programming (Templates) with C++
    • Clean Code with Modern C++
    • C++20

    Online Seminars (German)

    Contact Me

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *