MVC - 我该如何理解它?(Java)

4

最近我一直在阅读有关MVC的内容,因为我需要学习如何将模型和GUI分离,以便完成学校项目。(老师没有讲解,所以我正在自学。)

我相信我已经理解了基本原理,即视图通过操作监听器“注册”到模型上,或者类似这样的操作。但是我就是无法将其转化为代码。我看了几个小程序,但它们对于我来说仍然过于复杂,难以理解基础知识。

能否有人像向5岁孩子解释一样地给我解释一下,或者给我一些链接或一个非常简单的示例程序之类的东西?非常感谢!


1
请注意,您正在询问与Web MVC不同的GUI MVC(http://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller#Implementations_of_MVC_as_GUI_frameworks)。我敢打赌,您将得到的大多数答案都是关于后者的。 - Javier
@Javier:不用担心 :) 我通过“actionlisteners”找到了他的问题。 - Goran Jovic
@Rickard:一定要看看zengr的答案。当你放弃Swing开始编写Web应用程序时,它会派上用场。 - Goran Jovic
3个回答

4

首先,您需要了解观察者模式。

基本思想是定义一个对象,在其发生变化时通知相关方。(请注意,观察者模式更为通用 - Observable会通知Observer关于“某个事件”的信息。对于MVC,该事件是“某些内容已更改”)

首先,您需要定义OBSERVABLE和OBSERVER之间的协议。

package aaa;

// AN OBSERVER INTERFACE
// This is a contract between an interested party (the OBSERVER) and
//   the thing it would like to know has changed (the OBSERVABLE)
// The OBSERVABLE will call this method whenever its data changes
public interface SomethingChangedListener {
    void somethingChanged(String name, Object newValue);
}

然后,您定义OBSERVABLE(可观察对象)

package aaa;

import java.util.ArrayList;
import java.util.List;

// An OBSERVABLE class
public class Person {
    // STEP 1: keep track of "who cares"
    //         outsiders with interest implement the observer interface
    //         and register with the person to indicate that they care
    private List<SomethingChangedListener> listeners = new ArrayList<SomethingChangedListener>();
    public void addSomethingChangedListener(SomethingChangedListener scl) {
        listeners.add(scl);
    }
    public void removeSomethingChangedListener(SomethingChangedListener scl) {
        listeners.remove(scl);
    }

    // STEP 2: be able to notify those observers by calling a method in the observer interface
    protected void fireSomethingChanged(String name, Object newValue) {
        for (SomethingChangedListener scl : listeners) {
            scl.somethingChanged(name, newValue);
        }
    }

    // STEP 3: whenever the data changes, notify the observers
    private String name;
    private int age;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
        fireSomethingChanged("age", name);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
        fireSomethingChanged("name", name);
    }
}

这使得任何其他对象只要实现了观察者接口,就可以“监听”它感兴趣的事物的变化。
例如:
package aaa;

public class Test1 {
    private static class TestObserver implements SomethingChangedListener {
        @Override public void somethingChanged(String name, Object newValue) {
            System.out.println("Property '" + name + "' changed to '" + newValue + "'");
        }
    }
    public static void main(String[] args) {
        Person p = new Person();
        p.addSomethingChangedListener(new TestObserver());
        p.setName("Scott");
        p.setAge(43);
    }
}

创建一个可观察对象(Person),注册一个观察者(TestObserver),然后与可观察对象交互。运行时,我们会看到:

Property 'name' changed to 'Scott'
Property 'age' changed to 'Scott'

到目前为止,一切都很好。

现在让我们把“Person”称为我们的“MODEL”。MODEL代表我们想要操作和查看的数据。

我们可以在“用户界面”中完成这个过程。用户界面可以是以下任何一种或多种形式:

  • 图形用户界面(GUI)
  • 命令行界面(CLI)
  • Web应用程序(HTML、JSP、ASP等)
  • Web服务(SOAP、REST等)

每种类型的用户界面(UI)的代码可能会有很大的不同,但概念是相同的。

用户界面(UI)允许用户(例如人或另一台计算机)查看模型的信息并对该信息进行更改。

“View”是UI的一部分,它显示用户的信息。它从模型中读取数据并以某种方式格式化以呈现。

如果模型发生变化,则必须更新View。为了实现这一点,它向模型注册观察者。这些观察者只需刷新View中相关部分的演示即可。

那么如果用户想要进行更改怎么办?

我们在UI中定义一个“Controller”,它是解释用户与UI交互的代码。例如,如果用户在“名称”字段中输入,控制器可以将其解释为“将'名称'的值更改为用户键入的文本。控制器进行了

person.setName(textFromField);

调用更新模型的方法。

还记得setName()方法的作用吗?它会通知观察者发生了变化。这将导致UI更新其视图。

请注意,“视图”和“控制器”不需要是单独的类;它们经常被合并在一起。真正重要的是理解“视图”(显示模型数据的UI部分)和“控制器”(解释用户交互并更新模型的UI部分)的角色。

在某些情况下,例如Web应用程序,视图和控制器是非常独立的。控制器解释发送到服务器的HTTP请求并更新模式。视图呈现HTML响应给用户。(如果您正在进行AJAX应用程序,则设计与GUI有点相似)

MVC分离(模型vs UI)的酷炫之处在于,您可以随时向模型添加或删除UI,并且可以在同一模型上拥有多个UI。如果在一个UI中更改了数据,则所有其他UI都会更新以反映更改。

很酷,对吧?


哇,谢谢,现在我们可以开始了!但控制器应该放在哪里呢?放在UI类中?还是单独的一个类中?或者由我来决定? - Rickard
“视图”和“控制器”可以是分开的类,也可以是合并在一起的。通常将它们放在一起更容易,并且将它们分开并不总是允许重用。(尽管在Web MVC设置中,它们是非常独立的)。 - Scott Stanchfield
如果Person类有一个“House”类、一个“Pet”类等等,那么我需要为每个类创建一个监听器列表,对吗? - Rickard
这要看情况... 你可以在 UI 中为模型中的每个部分设置监听器,或者你可以让 Person 监听其组成部分并代表它们发送更改消息。或者,你可以定义一个更高级别的 Model 类来跟踪所有较低级别的模型对象并报告它们的更改。这是其中一种 "情况各异" 的情况,我已经用过几种方法。请注意,通常最好监听单个部分,这样你就可以有特定的子 UI 来处理特定的部分(比如 Pet 编辑窗口)。 - Scott Stanchfield

4
现在,MVC 的基本原则是将 M、V 和 C 分离。
一方面,您有负责执行某些操作的类和代码,例如计算两个输入数字的总和或对数据库进行操作。这就是您的 Model。
另一方面,您有一些其他类,负责表单、文本输入、按钮以及屏幕上可见的其他内容。这就是 View。
现在,Controller 是连接这两者的一个部分。通常(比如说 Swing),您会让控制器实现 Listener 接口,然后将控制器添加为您希望软件对其做出反应的屏幕组件的监听器(比如按钮)。而实现的处理程序方法除了调用您在 Model 中实现的任何方法之外,不会做任何其他事情。
希望这样更加清晰明了。

我认为控制器执行了两个输入的求和计算,而模型则保存了这两个值是什么。 - Steven
不是的..这正是重点所在。控制器除了“转发”东西之外什么也不做..保持值和计算通常在模型中完成。“保持值”部分通常称为领域模型,而“计算”部分则是服务层。 - Goran Jovic
它确实让我更清楚了,尽管我仍然不明白如何从控制器向模型实现监听器。 - Rickard
这里有一些示例:http://life.csu.edu.au/java-tut/uiswing/learn/index.html。只需确保有一个单独的类(控制器)实现监听器接口,然后不要将“this”对象作为监听器添加,而是添加控制器。 - Goran Jovic
一定要亲自尝试教程中的解决方案。即使它与理论上的MVC不符合,这将是值得的经验。记住,在最终编程中,关键在于实践。 - Goran Jovic

2
这个图示可以帮助您理解Java中MVC的关系。
UI组件是View,控制servlet(只需在此处捕获所有传入的请求并将其重定向到正确的业务对象)是Controller,而业务对象是Model。
这是我对Java中MVC的解释。欢迎其他答案。 alt text

我在那个主题中根本看不到任何图形,太奇怪了!虽然我很想看到它。 - Rickard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接