C ++ dependency injection polymerization

I have a question about best dependency injection techniques with polymorphic classes. I am new to C ++, so please forgive me if this is an obvious question. Let's say I have a Runner class that needs to take two objects: Logger and Worker. Logger is an abstract class with two children, for example FileLogger and SocketLogger. Similarly, a worker is an abstract class with two children, such as ApproximateWorker and CompleteWorker.

The Runner class will be created from main () and will create a Logger and Worker based on a configuration file or something similar. I read a lot in SO and other places, and it seems that the general opinion is to prefer stacked objects and pass them by reference. However, I'm not quite sure how to manage the dynamic creation of objects like this. If I used allocated heaps of objects, I could do something like:

Logger* log;
Worker* worker;
if (/*user wants a file logger*/ {
    log = new FileLogger();
} else {
    log = new SocketLogger();
}
if (/* user wants an approximate worker*/) {
    worker = new ApproximateWorker();
} else {
    worker = new CompleteWorker();
}
Runner runner = Runner(log, worker);
runner.run();

Since I just keep pointers on the stack, I can handle various cases for Logger and Worker myself. If I use objects related to the stack, the only thing I can think of is to do something like this:

if (/*file logger and approx worker*/) {
    FileLogger log();
    ApproximateWorker worker();
    Runner runner = Runner(log, worker);
} else if (/*file logger and complete worker*/) {
    FileLogger log();
    CompleteWorker worker();
    Runner runner = Runner(log, worker);
} else if (/*socket logger and approx worker*/) {
    SocketLogger log();
    ApproximateWorker worker();
    Runner runner = Runner(log, worker);
} else {
    SocketLogger log();
    CompleteWorker worker();
    Runner runner = Runner(log, worker);
}

, , . , - .

- ? (, )?

+4
2

Runner ( ), . . , .

, factory . WHAT ( ) HOW ( ). .

// Factory.h
class tAbstractFactory
{
public:
   virtual Logger* getLogger() = 0;
   virtual Worker* getWorker() = 0;
};

template<typename loggerClass, typename workerClass>
class tConcreteFactory: public tAbstractFactory
{
public:
   loggerClass* getLogger() { return new loggerClass; }
   workerClass* getWorker() { return new workerClass; }
};

// Runner.h
class Runner
{
public:
   Runner(tAbstractFactory &fa)
   {
      m_logger = fa.getLogger();
      m_worker = fa.getWorker();
   }
private:
   Logger *m_logger;
   Worker *m_worker;
};

// Factory.cpp
tAbstractFactory &getFactory(int sel)
{
   if (sel == 1)
   {
      static tConcreteFactory<FileLogger, ApproximateWorker> fa;
      return fa;
   }
   else if (sel == 2)
   {
      static tConcreteFactory<FileLogger, CompleteWorker> fa;
      return fa;
   }
   else if (sel == 3)
   {
      static tConcreteFactory<SocketLogger, ApproximateWorker> fa;
      return fa; 
   }
   else
   {
      static tConcreteFactory<SocketLogger, CompleteWorker> fa;
      return fa; 
   }
}

// Client.cpp
Runner runner(fac);

Edit:

, :

  • Logger/Worker, Client.cpp . , Factory.cpp, ( ) .

  • Runner factory. , Runner, Logger, Worker ..

, . , /, , .

+2

, .

, (, ) . . unique_ptr , RAII .

#include <iostream>
#include <memory>
#include <exception>

struct Logger{
    virtual void log() =0;
}; 
struct Logger1 : Logger {
    void log() override { std::cout << " l1 " << std::endl;} 
};
struct Logger2 : Logger {
    void log() override { std::cout << " l2 " << std::endl;} 
};
struct Logger3 : Logger {
    void log() override { std::cout << " l3 " << std::endl;} 
};

struct Worker{
    virtual void work() =0;
};
struct Worker1 : Worker{
    void work() override { std::cout << " w1 " << std::endl;} 
};
struct Worker2 : Worker{
    void work() override { std::cout << " w2 " << std::endl;} 
};
struct Worker3 : Worker{
    void work() override { std::cout << " w3 " << std::endl;} 
};

struct Runner{
   Runner(Worker& worker, Logger& logger): worker(worker),logger(logger) {};

   Worker& worker;
   Logger& logger;
   void run(){
      worker.work();
      logger.log();
   }
};


std::unique_ptr<Worker> mkUniqueWorker(int i){ 
    switch (i) {
        case 1: return std::make_unique<Worker1>() ;
        case 2: return std::make_unique<Worker2>() ;
        case 3: return std::make_unique<Worker3>() ;
        case 4: throw std::runtime_error("unknown worker");
   }
};
std::unique_ptr<Logger> mkUniqueLogger(int i){ 
    switch (i) {
        case 1: return std::make_unique<Logger1>() ;
        case 2: return std::make_unique<Logger2>() ;
        case 3: return std::make_unique<Logger3>() ;
        case 4: throw std::runtime_error("unknown logger");
   }
};

int main() {

    auto worker = mkUniqueWorker(2);
    auto logger = mkUniqueLogger(3);
    Runner runner = Runner(*worker, *logger);
    runner.run();

    return 0;
}
0

Source: https://habr.com/ru/post/1599616/


All Articles