我有一个简单的问题,我甚至不确定它有答案但我们试试。 我用C ++编写代码,并使用依赖注入来避免全局状态。这非常有效,并且我不经常在意外/未定义的行为中运行。
然而我意识到,随着我的项目的增长,我正在编写很多我认为是样板的代码。更糟糕的是:事实上,有更多的样板代码比实际代码更难以理解。
没有比这更好的例子了,让我们走吧:
我有一个名为TimeFactory的类,它创建了Time对象。
更多细节(不确定它是否相关):时间对象非常复杂,因为时间可以有不同的格式,它们之间的转换既不是线性的,也不是直接的。每个“时间”包含一个同步器来处理转换,并确保它们具有相同的,正确初始化的同步器,我使用TimeFactory。 TimeFactory只有一个实例并且是应用程序范围的,所以它有资格获得单身,但是,因为它是可变的,我不想让它成为单身
在我的应用程序中,很多类需要创建Time对象。有时这些类是深层嵌套的。
假设我有一个包含B类实例的A类,依此类推至D类.D类需要创建Time对象。
在我天真的实现中,我将TimeFactory传递给A类的构造函数,它将它传递给B类的构造函数,依此类推,直到D类。
现在,假设我有几个类,如TimeFactory和几个类层次结构,如上所述:我放弃了我想要使用依赖注入的所有灵活性和可读性。
我开始怀疑我的应用程序中是否存在重大设计缺陷...... 或者这是使用依赖注入的必要之恶?
你怎么看?答案 0 :(得分:4)
在我天真的实现中,我将TimeFactory传递给构造函数 A类,它将它传递给B类的构造函数,依此类推 直到D级。
这是依赖注入的常见误用。除非A类直接使用TimeFactory,否则它不应该看到,知道或有权访问TimeFactory。 D实例应该使用TimeFactory构造。然后应该使用刚刚构造的D实例构造C实例。然后是B与C,最后是A与B.现在你有一个A实例间接地拥有一个可以访问TimeFactory的D实例,而A实例从未看到TimeFactory直接传递给它。
MiškoHevery在this video中谈到这一点。
答案 1 :(得分:1)
全球国家并非总是邪恶的,如果它真的是全球性的。通常存在工程权衡,并且使用依赖注入已经引入了比使用单例接口或全局变量更多的耦合:类A现在知道比类B需要TimeFactory
,这通常更详细B级比A级要求。 B类和C类以及C类和D类也是如此。
考虑以下使用单例模式的解决方案:
让TimeFactory
(抽象)基类提供对应用程序的`TimeFactory的单例访问:
将该单例设置为TimeFactory
的具体子类
让TimeFactory
的所有访问都使用该单例。
这会创建全局状态,但会将该全局状态的客户端与其实现的任何知识分离。
以下是潜在实施的草图:
class TimeFactory
{
public:
// ...
static TimeFactory* getSingleton(void) { return singleton; }
// ...
protected:
void setAsSingleton(void)
{
if (singleton != NULL) {
// handle case where multiple TimeFactory implementations are created
throw std::exception(); // possibly by throwing
}
singleton = this;
}
private:
static TimeFactory* singleton = NULL;
};
每次实例化TimeFactory
的子类时,您都可以在其构造函数或其他位置调用setAsSingleton
。