【问题标题】:Coupling between controller and view控制器和视图之间的耦合
【发布时间】:2011-02-05 01:42:09
【问题描述】:

对我来说,一个好的 MVC 实现的试金石是换出视图是多么容易。由于懒惰,我总是做得非常糟糕,但现在我想把它做好。这是在 C++ 中,但如果我相信炒作的话,它应该同样适用于非桌面应用程序。

这里有一个例子:应用程序控制器必须在后台检查某个 URL 是否存在。它可能会连接到“URL 可用”事件(使用 Boost Signals),如下所示:

BackgroundUrlCheckerThread(Controller & controller)
{
   // ...
   signalUrlAvailable.connect(
      boost::bind(&Controller::urlAvailable,&controller,_1))
}

那么Controller::urlAvailable 长什么样子?

这是一种可能性:

void
Controller::urlAvailable(Url url)
{
    if(!view->askUser("URL available, wanna download it?"))
      return;
    else
      // Download the url in a new thread, repeat
}

在我看来,这似乎是视图和控制器的严重耦合。这样的耦合使得在使用 web 时无法实现视图(协程除外)。

另一种可能性:

void
Controller::urlAvailable(Url url)
{
   urlAvailableSignal(url); // Now, any view interested can do what it wants
}

我偏爱后者,但如果我这样做,似乎会有:

  1. 400 亿个这样的信号。对于非平凡的应用程序,应用程序控制器可能会变得很大
  2. 给定视图可能会意外忽略某些信号(API 可以在链接时通知您,但信号/槽是运行时)

那么您建议如何消除耦合并降低复杂性?提前致谢。

【问题讨论】:

    标签: c++ model-view-controller user-interface qt coupling


    【解决方案1】:

    对我来说,一个好的 MVC 实现的试金石是换出视图是多么容易。

    我可能会因为这样说而引火,但我不同意这种说法。这在纸面上看起来不错,但现实世界的例子表明,一个好的 UI 是响应式和交互式的,这通常需要将视图和控制器交织在一起。尝试编写一个完全通用的控制器来处理不可预见的理论视图会为控制器和视图增加大量代码和复杂性。根据我的经验,相互关联的视图/控制器效果更好 - 我认为它是“M(VC)”。

    我想说,一个好的 MVC 实现的试金石是你可以多么容易地将另一个视图/控制器对“添加”到模型中。从一个视图/控制器(例如桌面操作员)对模型的更改是否传播到另一个视图/控制器(例如 Web 远程用户)。该模型是否足够通用以支持不同的视图/控制器范例(例如桌面 GUI、命令行、计划/批量输入、基于 Web 的 UI、Web 服务等)?

    这并不是说控制器代码不能共享(例如,从一个公共库派生),但您必须在控制器应该处理的内容(模型的外部操作)之间找到正确的界限以及什么应该被视为模型行为的一部分(模型的内部转换)。查看代码同上。

    我认为阅读"What goes into the “Controller” in “MVC” 下的一些答案也会对此有所帮助。

    【讨论】:

    • 我确实在对 StackOverflow 的广泛调查中阅读了您的链接帖子。我不是说我不同意你。就我而言,是的,该模型足够通用。它目前支持桌面和 XML-RPC 视图。但是,我试图避免很多重复的代码。我认为 MVC 会有很大帮助。您将如何在相互关联的世界中重写上面的示例?
    • 我是说你的解决方案(“这......视图和控制器的耦合”)是自然的并且是可以预料的。要实现另一个视图,您可能还需要创建一个新的互连控制器来配合它。您可能需要重构以提取 2 个视图之间和 2 个控制器之间的公共代码(可能是基类)。我假设上面的控制器代码用于桌面应用程序,新视图用于非 UI XML-RPC 视图。您还将创建一个带有 urlAvailable() 回调的 XML-RPC 控制器,该控制器将在没有用户交互的情况下下载 URL。
    • 在这样的设置中,我认为控制器的逻辑为零或完全没有逻辑?也就是说,它只是将视图和模型连接起来?
    • 啊,这很难。我要去买东西。感谢您的建议 Bert F。​​我会告诉您进展如何。
    • 仅供参考,多亏了这个建议,我没有太努力地创建一个最终通用的控制器。此时,我有两个控制器:一个控制应用程序逻辑,另一个控制 GUI 逻辑。效果非常好,我很满意 :)
    【解决方案2】:

    您使用“m”来保持它们解耦 - 模型并使用(在概念上)类似命令的模式和侦听器模式来降低复杂性。

    所以你的控制器可能看起来像这样:

    void
    Controller::urlAvailable(Url url)
    {
       Controller::fireSignal("urlAvailable", url, ... other possible parameter);
    }
    Controller::fireSignal(char* cmd, Url url, ... other possible parameters) {
       Model &M   = new Model();
       M->command = cmd;
       M->url     = url;
       M-> ... other possible
       for(int v = Controller::ViewCount; --v >= 0; )
          Controller::Views[v]->notice(M);
    }
    

    注意:我不是 C++ 程序员,请原谅我的语法错误。

    MVC 的整个想法是使用 M(odel) 将 C(ontrol) 与 V(iew) 解耦。该示例非常简化。更实用的方法是对不同种类的相似信号使用不同的模型。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-10
      相关资源
      最近更新 更多