3

我正在开发一个应用程序,我使用的是MVC模式。目前我的计划是为每个窗口提供一个MVC模式。例如,我的登录窗口有自己的mvc系统。另一个他们选择的窗口有自己的mvc系统。然后主视图有自己的mvc系统...多窗口设计的mvc

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

我遇到的问题是,我如何获得窗口mvc中的日志以正确调用选择窗口,然后在他们做出选择之后,选择窗口如何调用mainview窗口?

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

+0

我将使用MVC复杂的任务开始,并没有那么多简单的“一次性”类型的任务。所有这一切(在我心中)的关键是连接。你想创建一些容易插入你的程序或任何其他需要此功能的类似程序,所以你需要限制代码的[耦合](http://en.wikipedia.org/wiki/Coupling_( computer_science))尽可能多。 –

+3

MVC是一种很好的模式,但严格执行MVC是一种反模式IMO。有意义时使用MVC,但不要为了MVC而做MVC。 –

+1

你可以看看这个[示例](http://stackoverflow.com/questions/3066590/gui-problem-after-rewriting-to-mvc/3072979#3072979)来帮助缩小你的问题。我会让模型状态的“已认证”部分,并让视图作出相应的反应。用模态对话框响应切换登录/注销按钮。 – trashgod

回答

3

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

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

+0

我明白这一点。但是,我从哪里启动窗口。我有控制器启动窗口,查看器还是模型?哪个最好?现在我只是让控制器启动窗口。 – prolink007

+3

让控制器启动窗口。该模型是数据和唯一的数据。视图是将模型呈现给用户的手段。模型和视图都不是你开窗的合理地点。 – DwB

+1

我不明白你的架构足够肯定,但绝对不是模型。只要确保任何新发布的窗口在首次可见之前就作为相关模型的观察者注册。 – trashgod

1

其实我相信我要做的是使用中介模式来控制这些视图之间的交互。这种方式的耦合是非常有限的,它得到了我想做的确切工作。让我知道你的想法。

+0

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

2

尽管它很简单,但MVC模式还有许多时间不了解。首先,您需要了解所有这些分离的组件应该包含哪些内容以及它们如何一起讨论。

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

  • 模型:模型能够处理数据集的状态,它通常实现一些函数为了保存并从文件中加载它的状态。模型是一个实际上不应该改变其状态的盒子,除非有人(管理员)要求它。再次,一个好的模型不知道视图甚至控制器。这只是它的方式。一个非常简单的模型就是一个String。您可以从文件加载字符串,将其保存到文件中,将其更改为大写字母,子字符串等。一个字符串有一个状态,不知道它的上下文,只是等待某个人使用它。如果模型知道你被欺骗的控制器,那么除非你实现了同一个控制器(即使使用接口这是一个糟糕的做法),该模型将不可重用。

  • 控制器:控制器实际上是您的程序,这是制定决策部分的部分。在哪儿 ?以及...你的main function已经是控制器。再次,所有的决定都被制成一个控制器。当有人点击一个按钮时,控制器中的一个方法被调用(而不是一个视图),当你想更新一个标签时,你可以从控制器上执行。所有的线程也应该被处理成控制器。你的模型正在加载一个大文件?创建一个线程进入控制器,要求模型加载图像,然后通知主线程进行一次(出话题,搜索事件循环)

多个窗口,我们在这里!

查看/控制器他们如何互相交流? 该视图的责任仅仅是为控制器提供事件(文本字段已更改,单击按钮等)并能够以我们想要的方式显示。例如window.setTitle(“myWindow”);我们可能会试图将东西放入应该放入控制器的视图中,反之亦然。

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

好吧,我想你明白所有的MVC,现在是时候谈谈,让所有这些组件连接在一起说话的正确方法:

应该如何的看法是通知控制器?其实这取决于你使用的语言。首先想到以下几点:

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

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

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

  • 谁将创建并显示视图?控制器!

  • 谁应该注册才能查看事件?控制器太...创建后

  • 如何?该视图的工作是在事件发生时进行广播。通常通过java中的ActionListener,objective-c中的观察者,lambda函数C++,javascript中的函数回调。并且控制器列出来,在java中实现正确的“回调”功能actionPerformed,选择器,或者如果您为视图提供了函数回调,则甚至可以直接使用回调函数。

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

  • 给每个窗口一个控制器:听起来不错,你不必在创建登录控制器对象实例时只有一个控制器(无论是一个模型还是视图...),可以创建并显示登录窗口为好。

  • 每个服务的模型的一个实例。单身人士设计模式就是这样,您可能只需要一个“身份验证模型”,因为您在同一个应用程序中登录或注销。因此,不是为依赖于登录状态的每个窗口创建多个登录模型实例,而是实际上只想要一个。您可以通过登录模型对象的静态实例来完成此操作。这被称为单身人士。

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

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

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

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

总结:

  • 无论是观点还是模型应该知道控制器
  • 只有控制器做他们两人知道
  • 注意他们都互相交谈,使用方法听众或通知,如果同一型号有多个控制器。
  • 的过程总是如下:
    • 控制器创建视图,并听取其
    • 的观点告诉控制器的动作发生
    • 控制器过程中的作用可能会问模型改变一些东西
    • 该模型给出了结果(通过听众或回调函数返回函数)
    • 控制器处理结果
    • 控制器用结果更新视图。