具有多窗口设计的 mvc

Posted

技术标签:

【中文标题】具有多窗口设计的 mvc【英文标题】:mvc with multiple windows design 【发布时间】:2011-12-06 07:21:09 【问题描述】:

我正在开发一个应用程序并且我正在使用 MVC 模式。目前我的计划是为每个出现的窗口创建一个 MVC 模式。例如,我的登录窗口有自己的 mvc 系统。他们进行选择的另一个窗口有自己的 mvc 系统。那么主视图就有了自己的mvc系统……

这似乎有点愚蠢。我想知道这是否正常,让每个窗口都有自己的 mvc?如果没有,我怎样才能更好地安排?

我遇到的麻烦是,我如何让登录窗口 mvc 正确调用选择窗口,然后在他们做出选择后,选择窗口如何调用主视图窗口?

感谢您的帮助!如果您需要更多信息,请告诉我。

【问题讨论】:

我会先使用 MVC 来处理复杂的任务,而不是简单的“一次性”类型的任务。这一切的关键(在我看来)是联系。您想要创建可以轻松插入您的程序或任何其他需要此功能的类似程序的东西,因此您需要尽可能限制代码的coupling。 MVC 是一个很棒的模式,但严格执行 MVC 是一种反模式 IMO。在有意义的情况下使用 MVC,但不要为了 MVC 而使用 MVC。 您可以查看此example 以帮助缩小您的问题。我会让authenticated 成为模型状态的一部分,并让视图做出相应的反应。使用模式对话框响应切换登录/注销按钮。 @trashgod Authenticated 是我的“个人资料”模型的模型状态的一部分。配置文件模型包含有关登录用户的信息。而且我认为由于“选择”窗口将与“配置文件”模型有关,我将使用“配置文件”模型作为 mvc“选择”的模型”。但是我如何正确地从登录窗口调用该选择窗口?我会使用控制器来调用那个“选择”窗口吗?这里的最佳做法是什么?谢谢 【参考方案1】:

尽管 MVC 模式很简单,但很多时候都没有被理解。首先,您需要了解所有这些分离的组件中应该包含哪些内容以及它们如何协同工作。

视图:任何 UI 元素。一个好的可重用视图元素应该在任何地方重用。因此视图不知道它的上下文,也不知道他将与之交互的特定控制器。视图知道的是它自己的状态和一个(或多个)通用侦听器,一旦发生某些操作,它将调用。视图元素知道它的状态,广播变化但不应该自己改变它的状态。例子:一个按钮会广播一个“点击”的动作,你会通过一个控制器通过aButton.setLabel("click me!");这样的方法来设置按钮的名字

模型:模型能够处理数据集的状态,它通常实现一些功能,以便将其状态保存到文件中或从文件中加载。模型是一个盒子,它实际上不应该改变它的状态,除非有人(控制器)要求它。同样,一个好的模型不知道视图甚至控制器。它就是这样。一个非常简单的模型例如是一个字符串。您可以从文件中加载字符串,将其保存到文件中,将其更改为大写、子字符串等。字符串确实有状态,不知道它的上下文,只是等待有人使用它。如果模型知道你被愚弄的控制器,那么除非你实现相同的控制器,否则模型将无法重用(即使使用接口也是一种不好的做法)。

控制器:控制器实际上是您的程序,这是做出决策的部分。在哪儿 ?好吧……您的main function 已经是控制器了。同样,所有决定都由控制器做出。当有人单击按钮时,会调用控制器中的方法(而不是视图方法),当您想要更新标签时,您可以从控制器中执行此操作。此外,所有线程都应处理到控制器中。您的模型正在加载一个大文件?在控制器中创建线程,让模型加载图片,完成后通知主线程(题外话,搜索事件循环)

多个窗口,我们来了!

视图/控制器如何相互交互? 视图的职责只是向控制器提供事件(文本字段更改、按钮被单击等)并能够以我们想要的方式显示。例如 window.setTitle("myWindow");我们可能会想把应该放在控制器中的东西放在视图中,反之亦然。

例如,如果您正在构建一个警报面板,您将有 2 个按钮(确定、取消)和一个用于文本的标签。一旦单击“取消”的“确定”,视图是否知道该怎么做?当然不是......但是一旦点击面板将不得不自行消失。相反,如果您确实创建了一个用于输入密码的面板,则视图将无法保持验证过程。这将是控制器同意或不同意提供的密码的工作。

好吧,我猜你已经理解了所有的 MVC,现在是时候讨论让所有这些组件一起交流的正确方法了:

视图应该如何通知控制器?实际上,这取决于您使用的语言。首先想到以下几点:

避免视图知道控制器。 视图不应该从控制器获取信息,也不应该调用它的方法。它将如何告诉控制器发生了什么事?通过观察者(或听众)

小括号:一个视图可以调用另一个视图的方法,如果它只是保持视觉正确的问题。例如,调整窗口大小将确保该窗口的所有组件(子视图)仍然正确显示,但可能不会通知控制器窗口改变了大小。

MVC之间的通信是关键!那么,它是如何工作的呢?

谁来创建和显示视图?控制器!

谁应该注册查看事件?控制器也是……创建后

如何?视图的工作是在事件发生时广播事件。通常通过 java 中的ActionListener、objective-c 中的观察者、lambda 函数 c++、javascript 中的函数回调。列出的控制器在 java 中实现正确的“回调”函数actionPerformed、选择器,或者如果您向视图提供了函数回调,甚至直接实现回调函数。

因此,当从控制器创建的视图触发事件时,控制器会收到通知并且必须处理此事件。如果控制器想要将某些内容更改为视图,这很容易,因为控制器始终拥有并知道视图。

对于每个窗口一个控制器:听起来不错,在创建登录控制器对象实例时,您不必只有一个控制器(既不是一个模型也不是视图……),可以创建并显示一个登录窗口也是。

每个服务一个模型实例。这里出现了单例设计模式,您可能只需要一个“身份验证模型”,因为您在同一个应用程序中登录或注销。因此,与其根据登录状态为每个窗口创建多个登录模型实例,不如只创建一个。您可以通过登录模型对象的静态实例来做到这一点。这称为单例。

现在如何确保将我们更改为模型的内容复制到所有控制器?简单的案例是一个控制器和一个模型。控制器正在更改模型并等待模型被更改然后使用更改(例如将其显示到视图中)。如果模型通过多个控制器共享会发生什么?例如,模型是一个登录服务,你有两种状态:登录和注销。 2 个控制器正在使用这个模型,每个控制器都有自己的视图(窗口),一个只是显示密码字段,第二个是一个带有图像的窗口,只有在用户登录时才会显示。有这种情况的解决方案不是一种,而是几种。一种是使用聆听/观察模式。几个控制器可以监听模型的变化。一旦其中一个正在更改它,所有控制器都将收到更改通知,因此将更新视图。另一种方法是保留一个通知中心(这是另一种模式),让控制器或模型广播发生的事情,然后所有对此事件感兴趣的控制器都会收到更改通知并处理该事件。对于模型可以随时更改(当然是通过隐藏在某处的控制器)的情况,最后一种解决方案特别有趣。通知中心正在使用事件循环,并且大部分时间都在尝试通知到主事件循环(应该是你的 UI 控制器的线程)

因此,对于我们的示例,如果登录过程处于脱机状态,我将使用一个简单的侦听器。 2 个控制器正在监听模型更改。一个控制器输入密码后会调用方法的模型login.loginWithPassword(String some password),如果密码正确,模型会广播一个事件“loginStateChanged”。然后所有的监听器(包括我们的 2 个控制器)都会收到这个事件并告诉视图更新我们想要的任何东西(例如显示只能在登录时显示的图像)。

在上面的例子中,我们的控制器询问模型并且模型直接触发事件,这在控制器的线程中,这很好,因为没有并发访问的风险(相同的线程=没问题)但是:

如果登录是远程登录,例如在线服务认证,我们将敲响服务器的门并等待它的回答。由于此过程可能需要几秒钟,因此控制器会一直等待服务器的回答。为了避免这种情况,我们应该将请求发送到另一个线程,并用一条消息(等待服务器)修改视图,直到我们从服务器得到答案。一旦我们得到答案(或者如果没有答案,则在超时之后),我们之前创建的线程将得到模型的答案是真还是假。一个非常糟糕的主意是直接用结果更新视图。问题是得到答案的线程,没有运行到主控制器的线程中,因此如果我们更新视图,我们确实面临并发访问(如果视图对象恰好在同一时间被 2 个线程更改遇到崩溃)。确保我们不会弄乱主线程的一种方法是简单地将模型的答案通过将在主事件循环中分派的通知发送到主线程。在这种情况下,尤其是我将使用通知中心而不是模型中的简单广播。有趣的是,一些框架在主事件循环中提供广播。使用块或 lambda 函数有更好的解决方案,但它超出了这个问题的范围。

总结一下:

视图和模型都不应该知道控制器 只有控制器知道它们两个 注意他们相互交谈的方式,如果同一模型有多个控制器,请使用侦听器或通知。 过程始终如下: 控制器创建视图并监听它 视图告诉控制器一个动作发生了 控制器处理操作可能会要求模型更改某些内容 模型给出结果(通过监听器或回调返回函数) 控制器处理结果 控制器使用结果更新视图。

【讨论】:

【参考方案2】:

如何从登录窗口正确调用该选择窗口?

使用observer pattern。如果任何视图更改了模型的状态,那么所有注册的侦听器都会收到通知,并且每个侦听器都可以更新自身以反映更改。这个example 提到了实现观察者模式的三种常用方法。

【讨论】:

我明白这一点。但是我从哪里启动窗口。我是否让控制器启动窗口、查看器或模型?哪个最好?现在我只是让控制器启动窗口。 让控制器启动窗口。模型是数据并且只是数据。视图是将模型呈现给用户的一种方式。模型和视图都不是启动窗口的合理位置。 我不太了解您的架构,无法确定,但绝对不是模型。只需确保任何新启动的窗口在第一次可见之前都已注册为相关模型的观察者。【参考方案3】:

其实我相信我要做的是使用一个中介者模式来控制这些视图之间的交互。这样,耦合非常有限,它可以完成我想要的确切工作。让我知道你的想法。

【讨论】:

是的,mediator 是一个很好的解决方案,实际上通知中心主要用作全局,在主线程中。

以上是关于具有多窗口设计的 mvc的主要内容,如果未能解决你的问题,请参考以下文章

java 159.具有最多两个不同字符的最长子串(#1滑动窗口).java

java 159.具有最多两个不同字符的最长子串(#1滑动窗口).java

java 159.具有最多两个不同字符的最长子串(#1滑动窗口).java

java 159.具有最多两个不同字符的最长子串(#1滑动窗口).java

java 159.具有最多两个不同字符的最长子串(#1滑动窗口).java

设计一个多文档界面(MDI)应用程序,可以实现打开排列和关闭子窗口。