一遍又一遍地传递对象的替代方法

时间:2013-03-14 10:45:35

标签: c++ interface singleton dependencies

有几个类经常被其他类派生。它们是用户输入接收器,如按键接收器,按键释放接收器,鼠标移动接收器等。从那些继承的类是例如热键动作,其继承自键下接收器和键释放接收器,或者从鼠标移动接收器继承的相机旋转器。用户所做的输入将由另一个类转发到相应的接收器,因此任何输入接收器,例如,相机旋转器需要在该转发类中注册自己。转发输入的类总是相同的,从来没有两个。

当我的班级用户创建一个新的输入接收器时,一个继承自例如鼠标移动接收器,鼠标移动接收器需要知道转发类,以便自己注册接收鼠标输入。所以鼠标移动接收器构造函数的代码如下所示:

mouseMoveReceiverIF(inputforwarder* ifo)
{
    ifo.registerInputReceiver(this);
}

由于很明显只有一个输入转发器,如果​​用户必须通过它,它不仅不太直观,而且还为每个输入接收器创建了额外的输入。

但是,如果我将输入转发器设为单例或全局变量,以便输入接收器可以访问它而无需用户在构造函数中传递它,我还有其他问题:然后用户需要要知道在创建任何输入转发器之前输入转发器必须存在。什么都没有警告他,没有什么会迫使他先创建输入输入器。该程序将简单地崩溃,因为输入接收器将尝试向不存在的输入转发器注册。那会更糟。

有效和正确的方式如何?什么是上述例子的替代品?

谢谢!

4 个答案:

答案 0 :(得分:2)

首先,我会问你是否应该隐藏用户的InputForwarder实例。调用这些构造函数的用户代码正在配置系统并设置依赖项 - 很可能代码应该显式地声明这些依赖项,即使这意味着键入一个额外的构造函数参数。

假设您要隐藏它,它取决于输入转发器的创建方式:

  • 如果可以在没有参数的情况下创建,请将其设置为自启动服务。也就是说,你有一个返回“the”实例的函数,如果在调用函数时它不存在,那么函数就会创建它。

  • 如果它采用用户提供的参数,则它不能是自启动服务。您可以提供由用户设置的全局“默认输入转发器实例”,然后在构造函数中检查是否存在此实例 - 如果它在那里注册或者引发异常。

如果您担心,可测试性在这里可能不是问题。即使您有一个全局“默认”实例来隐藏普通用户的依赖关系,您仍然可以使用这样的构造函数:

mouseMoveReceiverIF(inputforwarder* ifo = 0)
{
    if (!ifo) {
        ifo = getDefaultIfo();
        // check for null again if necessary
    }
    ifo.registerInputReceiver(this);
}

所以你可以注入任何适合测试的输入转发器。

答案 1 :(得分:1)

您可以创建一个输入转发器的单例, 并确保在程序启动时初始化它。 因此,现在任何客户端都无法在创建它之前访问它。

关于您的设计还有一件事: 您可能需要考虑使用Decorator模式来实现reciver的专业化。

修改 如果你不想要单例模式(因为单元测试等......) 比工厂方法更有效, 类似于:InputReceiverFactory.GetInputReceiver()

答案 2 :(得分:1)

2条建议:

1)创建一个Init函数/静态类成员,为您创建输入转发器。这是一种很好的封装形式。

2)使inputforwarder类成为一个静态类,包含所有静态成员和私有构造函数。如果需要初始化任何内容,您可以使用init类方法执行此操作。

希望这会有所帮助。我听说静态类有时是一种不好的做法,但如果你不想恢复功能,名称空间将是唯一的其他解决方案。 (我知道)

答案 3 :(得分:0)

如果我理解你的设计,你可以通过模板制作课程的inputforwarder部分吗?然后你可以自动让它调用正确的转发器。

就我个人而言,我会远离单身人士 - 他们往往难以进行单元测试。