c依赖注入多态性

我有一个关于依赖注入多态类的最佳实践的问题.我是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等更改的影响.

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

点赞