8

我想知道使用依赖注入的 C++ 方式是什么?那是使用模板还是多态类?考虑以下代码,

class AbstractReader
{
public:
  virtual void Read() = 0;
};

class XMLReader : public AbstractReader
{
public:
  void Read()  { std::cout << "Reading with a XML reader" << std::endl; }
};

class TextFileReader : public AbstractReader
{
public:
  void Read()  { std::cout << "Reading with a Text file reader" << std::endl; }
};

class Parser
{
public:
  Parser(AbstractReader* p_reader) : reader(p_reader) { }
  void StartParsing() { reader->Read();
    // other parsing logic
  }
private:
  AbstractReader* reader;
};

template<class T>
class GenericParser
{
public:
  GenericParser(T* p_reader) : reader(p_reader) { }

  void StartParsing()
  {
    reader->Read();
  }
private:
  T* reader;
};

1 - 哪种方法最好?通用解析器还是解析器?我知道如果是GenericParser,可以删除继承。

2 - 如果模板是要走的路,可以将所有代码写在头文件中吗?我见过许多使用模板的类将所有代码写入头文件而不是 .h/.cpp 组合。这样做有什么问题吗,比如内联等?

有什么想法吗?

4

4 回答 4

15

根据您希望如何构建代码或头文件,您没有自由选择。答案取决于您的应用程序的要求。

这取决于耦合是否可以在编译时确定或必须延迟到运行时

如果组件及其依赖项之间的耦合是在编译时永久确定的,则可以使用模板。然后编译器将能够执行内联。

但是,如果需要在运行时决定耦合(例如,用户选择哪个其他组件将提供依赖项,可能通过配置文件),那么您不能为此使用模板,您必须使用运行时多态机制。如果是这样,您的选择包括虚函数、函数指针或std::function.

于 2009-05-31T07:07:17.903 回答
5

如果我在编译时知道阅读器的类型,我个人更喜欢使用模板解决方案,因为我觉得这里没有运行时决定,因此多态性将没有用。就在头文件中编写模板而言,您必须这样做以避免出现链接器错误。这是因为如果您在 cpp 中编写模板方法,编译器将无法实例化模板类,因此链接器会出错。尽管存在一些变通方法,但大多数模板代码都编写在头文件中。

于 2009-05-31T06:56:50.000 回答
2

至于1.“最好”是相对的。两种方法都有其优点和缺点。模板提供了原始速度,但不可避免地会内联更多代码(产生更多耦合),并且错误消息难以阅读。继承速度较慢并使对象更大,但它不需要内联(耦合较少)。它还具有相对更好的错误消息。

对于小型库来说,耦合的问题不大,模板可能是一个不错的选择。但是,随着库的复杂性增加,您需要转向耦合度较低的方法。如果您不确定您的库会增长到多大,或者不需要模板提供的速度(或者不想处理错误消息),请使用继承。

我对 2 的回答是 1 的后续。一些消费者模板需要内联,因此需要将代码放在标题中。这是一个耦合问题。内联增加了组件之间的耦合,并可能大大增加编译时间;避免它,除非您想要速度并确保您的库保持较小。

于 2009-05-31T06:38:40.690 回答
0

通用解析器还是解析器?

取决于代码的其余部分,通用解析器的问题是您要注入的类也必须是模板。
但是还有第三种更通用的方式...... boost::function 和 boost::lambda。您只需要一个function正确的(从类用户的角度来看)返回类型和参数。boost::function< void ()> reader = bind( &TextFile::read, reader ); 现在用户类独立于阅读器类,不必是模板。

class User
{
const boost::function< void ()>& reader;

public:
    void setReader( const boost::function< void ()>& reader ) 
    : reader(reader) {
    }
};

将所有代码写入头文件而不是 .h/.cpp 组合。

这就是所谓的分离模型,只有一种编译器支持它(Comeau 编译器)。开始阅读“出口”限制第 1 部分“出口”限制第 2 部分


@CiscoIPPhone 评论:通用解析器的问题是您要注入的类也必须是模板。

template<class T>
class GenericParser
{
public:
    GenericParser(T* p_reader) : reader(p_reader) { }

    void StartParsing()
    {
        reader->Read();
    }
private:
    T* reader;
};

// Now you have a GeniricParser Interface but your Parser is only usable for 
// TextFileReader 
class Parser
{
public:
    Parser( GenericParser<TextFileReader> p_reader) : reader(p_reader) { }
    void StartParsing() { 
        reader->Read();
    }
private:
    GenericParser<RealParser> reader;
};


//Solution is to make Parser also a template class
template<class T>
class Parser
{
public:
    Parser( GenericParser<T> p_reader) : reader(p_reader) { }
    void StartParsing() { 
        reader->Read();
    }
private:
    GenericParser<T> reader;
};
于 2009-05-31T07:00:42.533 回答