我想知道使用依赖注入的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 或 Parser ?我知道如果它是 GenericParser ,可以删除继承。
2 - 如果要使用模板,是否可以在头文件中写入所有代码?我见过许多使用模板的类将所有代码写入头文件而不是.h / .cpp组合。这样做有什么问题,比如内联等吗?
有什么想法吗?
答案 0 :(得分:10)
根据您希望如何构建代码或头文件,您没有自由选择。答案由您的申请要求决定。
取决于是否可以在编译时决定耦合,还是必须延迟到运行时。
如果在编译时永久决定组件及其依赖项之间的耦合,则可以使用模板。然后编译器将能够执行内联。
但是,如果需要在运行时决定耦合(例如,用户选择哪个其他组件将提供依赖项,可能通过配置文件),那么您不能使用模板,并且必须使用运行时多态机制。如果是这样,您的选择包括虚函数,函数指针或std::function
。
答案 1 :(得分:2)
如果我在编译时知道读者的类型,我个人更喜欢使用模板解决方案,因为我觉得这里没有运行时决定,因此多态性将毫无用处。就编写头文件中的模板而言,您必须这样做以避免出现链接器错误。这是因为如果在cpp中编写模板方法,编译器将无法实例化模板类,因此链接器将给出错误。虽然存在一些变通方法,但大多数模板代码都是用头文件编写的。
答案 2 :(得分:0)
至于1.“最佳”是相对的。这两种方法都有其优点和缺点。模板提供原始速度,但更多代码不可避免地内联(产生更多耦合),并且错误消息难以阅读。继承较慢并使对象变大,但它不需要内联(耦合较少)。它还有相对更好的错误消息。
对于小型库,耦合较少,模板可能是一个不错的选择。但是,随着库的复杂性增加,您需要采用不太耦合的方法。如果你不确定你的库有多大,或者不需要模板提供的速度(或者不想处理错误消息),那么继续使用。
我对2的回答是:1。一些消费者模板需要内联,因此需要在标题中放置代码。这是一个耦合问题。内联增加了组件之间的耦合,可以大大增加编译时间;除非你想要速度并且确保你的图书馆仍然很小,否则请避开它。
答案 3 :(得分:0)
GenericParser或Parser?
取决于代码的其余部分,通用解析器的问题是您要注入的类也必须是模板。
但是还有第三种更通用的方法...... 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编译器)。开始阅读"Export" Restriction part 1和"Export" Restrictions part 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;
};