我有一个关于依赖注入多态类的最佳实践的问题.我是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将以多态方式使用这些对象(通过基类接口访问派生对象),则应该传递指针或引用.堆栈和堆上有变量的优缺点.没有普遍规则,一个优先于另一个.
还有一件事,abstract factory pattern可能适合你的情况.它将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接口的客户端不会受Logger,Worker等更改的影响.
就个人而言,完全可以不将这种模式用于小代码库.在类/文件之间存在大量依赖关系的大型项目中,它将对编译时间和可伸缩性产生影响.