简体   繁体   English

在C ++桌面应用程序上下文中使用MVC进行导航/查看流

[英]Navigation/View flow with MVC in the context of a C++ desktop application

Let's imagine you have a fullscreen C++ desktop application that consists of several screens with each of them having a distinct function and a ViewController with appropriate models as well. 假设您有一个全屏C ++桌面应用程序,该应用程序由多个屏幕组成,每个屏幕具有不同的功能,以及一个具有适当模型的ViewController。 For example a set of the following very simplified screens: 例如,一组以下非常简化的屏幕:

  1. Quiz: The user is navigated through a set of multiple-choice questions. 测验:引导用户浏览一组多项选择题。
  2. Quiz Results with Statistics. 带有统计的测验结果。
  3. Information: The user is presented with information about a specific subject. 信息:向用户显示有关特定主题的信息。
  4. Menu (Quiz, Information, Exit) 菜单(测验,信息,退出)

Judging by the GRASP principle Information Expert , each ViewController will know best when it is finished and time to move to a new screen. 根据GRASP原则Information Expert的判断,每个ViewController将在完成时以及移动到新屏幕的时间上最清楚。 Yet by the same principle, it is not the right place to decide what the next screen should actually be. 但是,根据相同的原则,并不是确定下一个屏幕实际应该是什么的正确位置。 In this rather simple example, one could argue it would be okay but in a more complex application, it will undoubtedly lead to duplicated code and logic as well as higher coupling and lower cohesion. 在这个相当简单的示例中,人们可能会认为这是可以的,但是在更复杂的应用程序中,无疑会导致代码和逻辑重复,以及耦合度更高和凝聚力更低。 There is also the problem that you would have to create the new widget and controller within the current screen's ViewController which brings all sorts of new problems and at least by the Creator principle, it is not the right choice. 还有一个问题是,您将不得不在当前屏幕的ViewController中创建新的小部件和控制器,这会带来各种各样的新问题,至少根据Creator原则,这不是正确的选择。 You would have to introduce a Factory to alleviate some of the problems, amongst other things. 您将不得不引入Factory来缓解某些问题,尤其是其中的一些问题。

So, the next logical step is to introduce an ApplicationController with the sole responsibility of managing Views and their controllers including the navigation flow from one view to the next. 因此,下一个合乎逻辑的步骤是引入ApplicationController,其唯一职责是管理视图及其控制器,包括从一个视图到下一个视图的导航流程。

This still leaves one problem wide open in my opinion: How to signal the ApplicationController that it is time to move to a different screen and hand over the control to that object properly? 在我看来,这仍然留下了一个尚待解决的问题:如何向ApplicationController发出信号,是时候该移动到另一个屏幕并将控件正确地移交给该对象了?

One could use the Observer pattern, for example. 例如,可以使用观察者模式。 Yet what if you have an expensive View active at the moment and want that one destroyed once the new screen is active? 但是,如果您目前有一个昂贵的View处于活动状态,并且希望在新屏幕处于活动状态时将其销毁怎么办? If the current ViewController signals the ApplicationController that the next screen should go up, it can manage everything up to the point where it would destroy the currently active screen which it cannot do because the current call comes from exactly that object. 如果当前ViewController向ApplicationController发出信号,通知下一个屏幕应该向上,则它可以管理所有事务,直到销毁当前活动的屏幕为止,由于当前调用恰好来自该对象,所以它无法执行操作。 Apart from several other problems with that approach. 除了这种方法的其他几个问题。

So my question is (and sorry for all the verbose introduction :P): How do you properly implement a navigation flow from one fullscreen widget to a different one with MVC which solves the above problems, splits the responsibility between the View- and ApplicationController and is nicely object oriented in terms of coupling and cohesion? 所以我的问题是(对所有冗长的介绍很抱歉:P):您如何使用MVC正确地实现从一个全屏小部件到另一个小部件的导航流程,从而解决了上述问题,在View-和ApplicationController之间分配了责任,在耦合和内聚方面很好地面向对象吗?

Sometimes you miss one detail in your thought process and you open up a whole can of problems without even realizing the mistake you made. 有时,您会在思考过程中遗漏一个细节,从而打开了一整套问题,甚至没有意识到自己犯的错误。

In this case the detail was that you can naturally post asynchronous as well as synchronous events. 在这种情况下,细节是您可以自然地发布异步事件和同步事件。 If you have to make sure that you are no longer in the context of the event posting method, post an asynchronous event. 如果必须确保您不再处于事件发布方法的上下文中,请发布异步事件。 Once you receive that event in your handler, you can be sure that the context was left. 在处理程序中收到该事件后,就可以确定已保留上下文。 And for example you can safely delete the object, if you so desire. 例如,您可以根据需要安全地删除对象。 Naturally the event handler should not be in the context of the same object that you are trying to delete. 自然,事件处理程序不应位于您要删除的同一对象的上下文中。

For completeness: In Qt, you can specify for each signal/slot-connection you make with connect(), that it should be of type Qt::QueuedConnection. 为了完整起见:在Qt中,您可以为使用connect()建立的每个信号/插槽连接指定类型为Qt :: QueuedConnection。 If you raise a signal, it won't be delivered until the control is back to the thread's event loop. 如果发出信号,则在控件返回到线程的事件循环之前将不会传递该信号。 Normally, Qt::AutoConnection is used which delivers a signal at the time it is raised (Qt::DirectConnection) if the receiver is in the same thread or falls back to queuing that signal (Qt::QueuedConnection) if the receiver is in a different thread. 通常,如果接收方在同一线程中,则使用Qt :: AutoConnection在发出信号时发送信号(Qt :: DirectConnection),或者如果接收方在同一线程中则退回到排队该信号(Qt :: QueuedConnection)。一个不同的线程。

In wxWidgets you can queue events with wxEvtHandler::QueueEvent(wxEvent* event) which is available through the Application singleton for example. 在wxWidgets中,您可以使用wxEvtHandler :: QueueEvent(wxEvent * event)对事件进行排队,例如,可通过Application单例使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM