2

有几个类通常是由其他类派生的。它们是用户输入接收器,如按键接收器、按键释放接收器、鼠标移动接收器等。继承自这些类的类例如是 hotkey-action,它继承自 key down 接收器和 key release 接收器,或者一个 camera-rotator,它继承自 mouse move 接收器。用户所做的输入将由另一个类转发到相应的接收器,因此任何输入接收器,例如相机旋转器,都需要在该转发类中注册自己。转发输入的类总是相同的,永远不会有两个。

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

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

由于很明显只有一个输入转发器,如果​​用户必须传递它不仅不那么直观,而且对于创建的每个输入接收器来说都是额外的输入。

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

一个有效和正确的方法是怎样的?上述示例的替代方法是什么?

谢谢!

4

4 回答 4

2

首先,我会质疑您是否应该向用户隐藏 InputForwarder 的实例。调用这些构造函数的用户代码正在配置系统并设置依赖关系——代码很可能应该明确声明这些依赖关系,即使这意味着输入额外的构造函数参数。

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

  • 如果可以不带参数创建,就让它像自启动服务一样。也就是说,您有一个返回“该”实例的函数,如果在调用该函数时该实例不存在,则该函数将创建它。

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

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

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

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

于 2013-03-14T11:13:07.697 回答
1

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

关于您的设计的另一件事:您可能需要考虑使用装饰器模式来专门化接收者。

编辑: 如果你不想要一个单例模式(因为单元测试等......),那么工厂方法就可以解决问题,比如: InputReceiverFactory.GetInputReceiver()

于 2013-03-14T10:58:30.653 回答
1

2条建议:

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

2) 使 inputforwarder 类成为具有所有静态成员和私有构造函数的静态类。如果需要初始化任何内容,您可以使用 init 类方法来完成。

希望这可以帮助。我听说静态类有时是一种不好的做法,但如果您不想恢复到函数,命名空间将是唯一的其他解决方案。(据我所知)

于 2013-03-14T11:01:08.177 回答
0

如果我了解您的设计,您可以inputforwarder通过模板参与课程吗?然后你可以自动让它调用正确的转发器。

就我个人而言,我会远离单例——它们往往会使单元测试变得困难。

于 2013-03-14T10:49:01.217 回答