【问题标题】:MVC - do I need to use Controller in the View?MVC - 我需要在视图中使用控制器吗?
【发布时间】:2012-09-09 21:18:44
【问题描述】:

据我所知,在 MVC 的标准实现中,我们将控制器和模型传递给视图

但我有点不同意这个想法。我不希望我的视图同时了解控制器和模型(哦不。也许有时视图需要模型,但我确信他可以在不了解控制器的情况下生活)

我认为Controller应该管理View和Model,而Model不需要知道Controller和View;视图不需要知道控制器(我不排除模型,因为视图的某些实现需要知道模型才能监听模型的变化)。所以我的想法是视图不需要了解控制器

1.这是一个例子:

public class MyView implements ButtonClickListener {

    private Controller myController;
    private Button myButton;

    // I commented out the model because we dont need it now 
    // we are talking about using controller in the view

    public MyView(Controller c/*, Model m*/) {
        myController  = c;
        myButton      = new Button(); // lets say that it is "register" button
        myButton.setOnButtonClickListener(this);
    }

    public void setRegisterButtonText(String text) {
        myButton.setText(text);
    }

    @Override
    public void onClick() {
        myController.tellToModelToDoSomething();
    }

}

和控制器:

public MyController implements Controller {

     private Model model;
     private View view;

     public MyController(Model model) {

          this.model = model;
          this.view  = new MyView(this);

     }

     public void tellToModelToDoSomething() {
          model.doSomeActions();
     }


}

2. 现在我如何在不通过控制器的情况下查看此实现:

我的观点:

public class MyView {

    private Button myButton;

    public MyView() {
        myButton = new Button();
    }

    public void setRegisterButtonText(String text) {
        myButton.setText(text);
    }

    public void setOnRegisterButtonClick(final Command command) {
        myButton.setOnButtonClickListener(new ButtonClickListener() {
                            @Override
                            public void onClick() {
                                command.execute();
                            }
                         });
    }

}

“命令”界面:

public interface Command {

     void execute(/*also can handle extra params*/);

}

和控制器:

public MyController implements Controller {

 private Model model;
 private View view;

 public MyController(Model model) {

      this.model = model;
      this.view  = new MyView();

      view.setOnRegisterButtonClick(command);

 }

 public void tellToModelToDoSomething() {
      model.doSomeActions();
 }

 private Command command = new Command() {

     public void execute() {
          tellToModelToDoSomething();
     }

 };

}

那么为什么我认为在视图中使用控制器不好

我们正在混合控制器和视图实现,建立新的依赖关系。

另外我认为 View 应该只包含 VIEWS 和对它们的操作(并且使用控制器和他的一些方法已经看起来像逻辑)。

在第一个示例中,视图告诉控制器该做什么。你同意吗?它看起来像视图控制控制器!

在第二个示例中,控制器控制要做什么,并告诉视图如果单击某个按钮(只有视图知道它将是什么按钮)要做什么

我一直使用第二种方案,但是在阅读了一本关于 mvc 的新书后,说我们需要将控制器传递给视图,我有点困惑。

你能帮我理解为什么我错了并给我看一些例子吗?

【问题讨论】:

  • 我相信控制器和视图不会直接交互。他们通过模型进行交互。所以模型知道控制器和视图,但视图和控制器不知道对方。
  • 说了这么多。 MVC 有许多变体,在实践中的实际实现是不同的。
  • 你是对的..,你认为在这个例子中,哪个实现更好?
  • 我更喜欢第二种实现,但您的视图只是一个按钮,因此它不是代表按钮任何东西的模型。这就是为什么它不需要知道它的模型。但是,如果您的视图是一个表格,那么您的视图必须知道它的数据模型。
  • 另外,你不需要Command接口。在 Java swing 中有一个标准的概念,它叫做 ActionListener。这正是您尝试做的。

标签: java model-view-controller view model controller


【解决方案1】:

没有 MVC 标准,因为有很多实现。这是许多教科书中对 MVC 的一种解释:

这个解释中控制器的定义是它处理来自视图的事件,所以视图必须使用控制器。

在标准 MVC 中,模型包含并公开数据,控制器操作模型并接受来自视图的事件,视图呈现模型并为控制器生成事件。

MVC 被认为是一个事务系统,其中事务由事件发起。交易通常如下所示:

  1. 在视图上生成一个事件(例如按钮单击)。
  2. 事件信息从视图传递到控制器。
  3. 控制器调用模型上的方法来更改它(setter 和其他可能更新某些数据库的操作方法)。

这些第一步代表 V-C 链接和 M-C 链接。 V-C 的存在是因为事件从视图传递到控制器以进行处理,而不是视图直接处理它们。存在 M-C 链接是因为控制器根据触发的事件更新模型。

从这里开始,有两条路。第一个:

  1. 交易结束。
  2. 另外,模型会触发自己的事件以指示它已更改。
  3. 视图正在侦听模型并接收事件,并更新其模型表示以反映更改。

这第一条路径代表了对 M-V 链接的一种解释。 M-V 链接是 1) 视图从模型中获取数据信息,以及 2) 模型告诉视图在修改后进行更新。

第二条路径只有一步:一旦控制器处理了事件,视图会立即更新,只需刷新其所有 UI 元素。对 M-V 链接的这种解释是模型只是将其信息提供给视图,与上面第一条路径中 M-V 链接中的点 #1 相同。

以下是针对我描述的 MVC 架构修改的一些代码:

public class MyView implements View, ModelListener {

    private Button myButton;
    private Controller controller;

    public MyView(Controller controller, Model model) {
        myButton = new Button();
        myButton.setOnButtonClickListener(new ButtonClickListener() {
            @Override
            public void onClick() {
                controller.onRegisterButtonClick();
            }
        });
        this.controller = controller;
        model.addModelListener(this);
    }

    public void setRegisterButtonText(String text) {
        myButton.setText(text);
    }

    public void modelUpdated(Model model) {
        // Update view from model
    }
}

还有控制器:

public MyController implements Controller {

    private Model model;
    private View view;

    public MyController(Model model) {
        this.model = model;
        this.view  = new MyView(this, model);
    }

    private void manipulateModel() {
        model.doSomeActions();
    }

    public void onRegisterButtonClick() {
        maniuplateModel();
    }
}

然后是模型:

public class MyModel implements Model {
    private List<ModelListener> modelListeners = new ArrayList<ModelListener>();

    public void addModelListener(ModelListener ml) {
        if (!modelListeners.contains(ml)) {
            modelListeners.add(ml);
        }
    }

    public void removeModelListener(ModelListener ml) {
        modelListeners.remove(ml);
    }

    public void doSomeActions() {
        // Do something
        fireUpdate();
    }

    private void fireUpdate() {
        // Iterates backwards with indices in case listeners want to remove themselves
        for (int i = modelListeners.size() - 1; i >= 0; i-- {
            modelListener.modelUpdated(this);
        }
    }
}

ModelListener 很简单:

public interface ModelListener {
    void modelUpdated(Model model);
}

这只是一种解释。如果您想在不同部分之间进一步解耦,您应该查看Presentation, Abstraction, Control (PAC) pattern。它比 MVC 更解耦,也非常适合分布式系统。对于简单的 Web、移动、桌面应用程序来说,这有点过头了,但是一些客户端/服务器应用程序和大多数云应用程序可以从这种方法中受益。

在 PAC 中,您拥有三个部分:表示、抽象和控制,但抽象和表示(模型和视图)不会相互交互。相反,信息只进出控制模块。此外,您可以拥有多个 PAC 子模块,这些子模块只能通过它们的控件相互交互,从而为分布式系统提供良好的模式。基本上,控制模块是任何数据传输的主要枢纽。

基本上,您对 MVC 的解释可能与我或他们的不同。重要的是您选择一种架构模式并遵循它以保持您的代码在未来可维护。你是对的,有一些方法可以进一步解耦 MVC。其实你的例子有点像PAC,只不过不是去掉V-C链接,而是去掉M-V链接。

无论如何,遵循一个架构,记录你的架构(这样人们就知道你的解释是什么),不要偏离那个。

【讨论】:

  • 感谢您的回复。感谢您的建议,并会了解更多关于 PAC 模式的信息
  • 谁会在modelListeners ArrayList 中添加views MyModel 类?这会在MyController 中完成,而在MyController 类中你会在model. addModelListener(view) 中完成吗?
  • 当然,我认为控制器在这个例子中是合适的位置是的。所以在控制器构造函数中:model.addModelListener(view); 或者,你可以让View 接口扩展ModelListener,这样你就知道它永远是一个监听器。或者让View 接口实现一个返回其ModelListener 的方法。有很多选择。就像我说的,选择一个实现并坚持下去:)
  • @1290 请注意,它目前在我的答案示例的视图中。
猜你喜欢
  • 1970-01-01
  • 2012-01-12
  • 1970-01-01
  • 1970-01-01
  • 2020-08-01
  • 1970-01-01
  • 2012-01-31
  • 2013-03-25
  • 1970-01-01
相关资源
最近更新 更多