gpt4 book ai didi

C++依赖注入(inject)多态性

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:09:05 27 4
gpt4 key购买 nike

我有一个关于使用多态类进行依赖注入(inject)的最佳实践的问题。我是 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 将以多态方式使用这些对象(通过基类接口(interface)访问派生对象),您应该将指针或引用传递给它。栈上和堆上的变量各有利弊。没有普遍的规则说一个比另一个更受欢迎。

还有一点,abstract factory pattern可能适合你的情况。它将 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);

编辑:

我至少看到两个好处:

  1. 当您添加新案例或更改具体 Logger/Worker 的类型时,Client.cpp 不会受到影响。也就是说,您限制 Factory.cpp 内部的更改,以便客户端逻辑(实际上使用创建的对象)保持不变。

  2. Runner 仅编程为工厂界面。依赖于 Runner 接口(interface)的客户端不会受到 LoggerWorker 等变化的影响。

就个人而言,不将此模式用于小型代码库是完全可以的。在类/文件之间存在大量依赖关系的大型项目中,它会对编译时间和可伸缩性产生影响。

关于C++依赖注入(inject)多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31623384/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com