[英]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);
編輯:
我能看到至少兩個好處:
添加新案例或更改具體Logger / Worker的類型時,Client.cpp不會受到影響。 也就是說,您限制Factory.cpp內部的更改,以便客戶端邏輯(實際使用創建的對象)保持不變。
Runner
僅編程為工廠界面。 依賴於Runner
接口的客戶端不會受Logger
, Worker
等更改的影響。
就個人而言,完全可以不將這種模式用於小代碼庫。 在類/文件之間存在大量依賴關系的大型項目中,它將對編譯時間和可伸縮性產生影響。
共享或唯一指針可以提供幫助,但您仍然可以將對象的引用作為依賴注入變量。
你需要確保在跑步者之前不要銷毀對象(記錄器,工人)。 依賴注入請求工廠。 在這種情況下,我使用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.