簡體   English   中英

c ++依賴注入多態性

[英]c++ Dependency Injection Polymorphism

我有一個關於依賴注入多態類的最佳實踐的問題。 我是C ++的新手,所以如果這是一個顯而易見的問題,請原諒我。 假設我有一個類Runner,它需要接收兩個對象,一個Logger和一個Worker。 Logger是一個帶有兩個子節點的抽象類,比如FileLogger和SocketLogger。 同樣,Worker是一個有兩個孩子的抽象類,比如ApproximateWorker和CompleteWorker。

Runner類將從main()創建,並將根據配置文件或類似內容創建Logger和Worker。 我已經在SO和其他地方做了很多閱讀,一般的情緒似乎是更喜歡堆棧分配的對象並通過引用傳遞它們。 但是,我不太確定如何動態創建這樣的對象。 如果使用堆分配的對象,我可以這樣做:

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();

因為我只是將指針存儲在堆棧中,所以我可以獨立處理Logger和Worker的不同情況。 如果使用堆棧分配的對象,我唯一能想到的就是:

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);
}

顯然,如果要傳入兩個以上的對象,或者每個對象有兩個以上的子類,這很快就會變得荒謬。 我的理解是,對象切片會阻止你做類似於第一個片段的事情。

我錯過了一些明顯的東西嗎? 或者這是使用動態內存的情況(當然還有智能指針)?

如果Runner將以多態方式使用這些對象(通過基類接口訪問派生對象),則應該傳遞指針或引用。 堆棧和堆上有變量的優缺點。 沒有普遍規則,一個優先於另一個。

更重要的是, 抽象工廠模式可能適合您的情況。 它將HAT(使用的確切類型的對象)與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);

編輯:

我能看到至少兩個好處:

  1. 添加新案例或更改具體Logger / Worker的類型時,Client.cpp不會受到影響。 也就是說,您限制Factory.cpp內部的更改,以便客戶端邏輯(實際使用創建的對象)保持不變。

  2. Runner僅編程為工廠界面。 依賴於Runner接口的客戶端不會受LoggerWorker等更改的影響。

就個人而言,完全可以不將這種模式用於小代碼庫。 在類/文件之間存在大量依賴關系的大型項目中,它將對編譯時間和可伸縮性產生影響。

共享或唯一指針可以提供幫助,但您仍然可以將對象的引用作為依賴注入變量。

你需要確保在跑步者之前不要銷毀對象(記錄器,工人)。 依賴注入請求工廠。 在這種情況下,我使用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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM