Friday, February 24, 2023

Dependency Injection


Dependency Injection (DI) is a technique where an object's dependencies are provided to it, rather than the object creating or finding them on its own.

Constructor injection

Constructor injection is a design pattern used in object-oriented programming to decouple dependencies between classes. It involves injecting dependencies into a class through its constructor. This allows for better control over the initialization of the class and provides better testability.

Here's an example C++ code that demonstrates constructor injection:


#include <iostream>
#include <memory>

class ILogger 
{
public:
    virtual void log(const std::string& message) = 0;
    virtual ~ILogger() = default;
};

class ConsoleLogger: public ILogger 
{
public:
    void log(const std::string& message) override 
    {
        std::cout << message << std::endl;
    }
};

class User 
{
private:
    std::shared_ptr<ILogger> logger_;

public:
    User(const std::shared_ptr<ILogger>& logger) : logger_(logger) 
    {

    }

    void performAction() 
    {
        logger_->log("User is performing an action");
    }
};

int main() 
{
    auto logger = std::make_shared<ConsoleLogger>();
    User user(logger);
    user.performAction();
    return 0;
}

In this example, the User the class has a dependency on a logger, which is injected into the class through its constructor. The constructor takes an instance of the ILogger interface as a parameter, allowing the class to work with any implementation of the logger that adheres to the required contract.

In the main function, a ConsoleLogger instance is created and passed to the User constructor. The User instance then uses the logger to log a message when it performs an action. If we wanted to use a different logger implementation, we could simply create a new class that implements the ILogger interface and pass it to the User constructor. This allows for more flexibility in configuring the dependencies of the User class, and it ensures that the dependencies are properly initialized before the class is used.


Setter injection


Setter injection is a design pattern used in object-oriented programming to decouple dependencies between classes. It involves injecting dependencies into a class through setter methods instead of the constructor. This allows for more flexibility and easier testing, as dependencies can be swapped out at runtime.

Here's an example C++ code that demonstrates setter injection:

#include <iostream> #include <memory> class ILogger { public: virtual void log(const std::string& message) = 0; virtual ~ILogger() = default; }; class ConsoleLogger : public ILogger { public: void log(const std::string& message) override     { std::cout << message << std::endl; } }; class User { private: std::shared_ptr<ILogger> logger_; public: User() { } void setLogger(const std::shared_ptr<ILogger>& logger) { logger_ = logger; } void performAction() { logger_->log("User is performing an action"); } }; int main() { auto logger = std::make_shared<ConsoleLogger>(); User user; user.setLogger(logger); user.performAction(); return 0; }

In this example, the User the class has a dependency on a logger, which is injected into the class through a setter method called setLogger. Instead of passing the logger instance through the constructor, we can create the User instance first and then set the logger later using the setLogger method.

In the main function, an ConsoleLogger instance is created and passed to the User instance using the setLogger method. The User the instance then uses the logger to log a message when it performs an action. If we wanted to use a different logger implementation, we could simply create a new class that implements the ILogger interface and pass it to the setLogger method at runtime. This allows for more flexibility in configuring the dependencies of the User class.

IInterface injection

Interface injection

Interface injection is a design pattern used in object-oriented programming to decouple dependencies between classes. It involves defining an interface that defines the contract of the functionality required by a dependent class and then injecting that interface into the dependent class, instead of injecting a concrete implementation of the required functionality. This allows the dependent class to be more flexible and reusable, as it can work with any implementation of the interface that adheres to the required contract.

Here's an example C++ code that demonstrates interface injection:

#include <iostream> #include <memory> class ILogger { public: virtual void log(const std::string& message) = 0; virtual ~ILogger() = default; }; class ConsoleLogger : public ILogger { public: void log(const std::string& message) override     { std::cout << message << std::endl; } }; class User { private: std::shared_ptr<ILogger> logger_; public: User(const std::shared_ptr<ILogger>& logger) : logger_(logger)     {     } void performAction()     { logger_->log("User is performing an action"); } }; int main() { auto logger = std::make_shared<ConsoleLogger>(); User user(logger); user.performAction(); return 0; } 

In this example, there are two classes: ILogger and ConsoleLogger. ILogger is an interface that defines the contract for a logger, which is simply a log function that takes a string message as input. ConsoleLogger is a concrete implementation of the ILogger interface that logs messages to the console.

The User the class has a dependency on a logger, which is injected into the class via its constructor. Instead of injecting a concrete implementation of the logger, the constructor takes an instance of the ILogger interface, allowing the class to work with any implementation of the logger that adheres to the required contract.

In the main function, an ConsoleLogger instance is created and passed to the User constructor. The User the instance then uses the logger to log a message when it performs an action. If we wanted to use a different logger implementation, we could simply create a new class that implements the ILogger interface and pass it to the User constructor.

No comments:

Post a Comment

LeetCode C++ Cheat Sheet June

🎯 Core Patterns & Representative Questions 1. Arrays & Hashing Two Sum – hash map → O(n) Contains Duplicate , Product of A...