在MVC架构中公开Java组件(如JButton)的最佳方法是什么?

tjr*_*age 3 java model-view-controller swing private public

我目前正在用Java编写Blackjack应用程序,它需要用MVC架构编写.我的所有模型,视图和控制器都完全编码,一切都很完美.但是,我想知道将侦听器附加到组件的最佳方法是什么(例如,JButton).

我当前的设计声明视图中需要侦听器作为公共final的所有组件.然后它将视图传递给控制器​​.从那里,控制器具有完全的统治权来访问组件并添加动作侦听器.代码示例:

public class View extends JFrame {
    public final JButton myBtn1 = new JButton("Button 1");
    public final JButton myBtn2 = new JButton("Button 2");

    public View() {
        //constructor code here....
    }
}

public class Controller {
    private Model myModel;
    private View myView;

    public Controller(Model m, View v) {
        myModel = m;
        myView = v;

        //now add listeners to components...

        myView.myBtn1.addActionListener(new MyActionListener1());
        myView.myBtn2.addActionListener(new MyActionListener2());
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我的一个朋友已经宣布他的所有JButton都具有私有范围,然后在视图中使用公共方法向其各自的组件添加动作侦听器.对我来说,这似乎是浪费时间,只会增加不必要的代码.代码示例:

public class View extends JFrame {
    private JButton myBtn1 = new JButton("Button 1");
    private JButton myBtn2 = new JButton("Button 2");

    public View() {
        //constructor code here....
    }

    public void myBtn1AddActionListener(ActionListener a) {
        myBtn1.addActionListener(a);
    }

    public void myBtn2AddActionListener(ActionListener a) {
        myBtn2.addActionListener(a);
    }

    //etc etc...
}

public class Controller {
    private Model myModel;
    private View myView;

    public Controller(Model m, View v) {
        myModel = m;
        myView = v;

        //now add listeners to components...

        myView.myBtn1AddActionListener(new MyActionListener1());
        myView.myBtn2AddActionListener(new MyActionListener2());
    }
}
Run Code Online (Sandbox Code Playgroud)

那么,鉴于上述两种情况,哪种方式更好?

谢谢.

Hov*_*els 5

我当前的设计声明视图中需要侦听器作为公共final的所有组件.然后它将视图传递给控制器​​.从那里,控制器具有完全的统治权来访问组件并添加动作侦听器.代码示例:

不,不要不必要地暴露字段 - 你不应该为了MVC而牺牲封装.出于上述原因,您不应将组件公开.而是为视图提供控件可以调用的公共方法,或者让视图通过侦听器更改状态到模型.将控件传递给视图和控件的视图,所有交互都应该通过公共方法,而不是通过公开组件或字段.

我已经解决了这个问题的一种方法,我并不是说这是最好的或规范的方法是将Control的实例传递给View并为我的监听器使用匿名内部类,并让这些类调用控制方法.所以,例如,

// inside of view
exitButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent evt) {
      if (control != null) {
         control.exitAction();
      }
   }
});
Run Code Online (Sandbox Code Playgroud)


Rob*_*bin 5

注意:这个答案是我个人的意见.我仍然需要找到关于编写Swing UI并将其与业务逻辑联系起来的"最佳/最推荐"方式的体面文献.但在我发现之前,我会使用常识和个人经验.

我想说这两者都不是实现这一点的正确方法.在MVC中,视图只是您向用户提供可视信息的部分.但是,ActionListener当您按下按钮时触发的代码是属于控制器的代码,因为它很可能是业务逻辑(这是我根据您的代码片段做出的假设.如果您的按钮只执行UI操作,就像启用另一个组件,动作监听器应完全包含在视图中).

所以我会在控制器中公开这些方法,例如,如果你现在有ActionListener这样的话

public void actionPerformed( ActionEvent e ){
  doSomeStuff( UI info, event info );
}
Run Code Online (Sandbox Code Playgroud)

哪里UI info是从视图获得的event info信息和从事件中获得的信息,我将在控制器上介绍公共方法

public void doSomeStuff( UI info, event info )
Run Code Online (Sandbox Code Playgroud)

并从视图中调用该方法.这有两个主要好处:

  1. 从视图到控制器只有依赖关系,而不是从控制器到视图的依赖关系.如果控制器包含业务逻辑,那么您不希望将该代码重用于SwingUI.这里不需要依赖Swing类.一个例子可以是单元测试.如果您的控制器不依赖于UI,您可以轻松地测试应用程序的控制器模型部分,并使用API​​执行相同的代码路径,就像通过UI调用这些调用一样.
  2. 您可以完全调整UI,并决定将另一个组件替换为无法连接的按钮,ActionListener而无需重写控制器和视图.

编辑

虽然我认为我的帖子的上述部分仍然有效,但我必须承认,在阅读了trashgod的回答(他在其中一条评论中提到)中提到的维基百科MVC页面之后,上面只是对MVC模式的一种解释,如何在Swing应用程序中实现它.

查看该Wiki页面,它将MVC中的三个组件定义为(汇总):

  • Model:管理应用程序域的行为和数据
  • 视图:将模型渲染为适合交互的形式,通常是用户界面元素
  • 控制器:接收用户输入并通过调用模型对象来启动响应

我的解释/意见/首选方式是拥有一个模型和一个控制器,如果需要多个视图(或者例如测试零视图).控制器不直接接收用户输入(这相当于让它在视图上注册监听器),但提供API挂钩以将用户输入传递到.这提供了一种可以处理用户输入的"抽象控制器",但视图仍然需要将实际用户输入事件转换为控制器可理解的事件/信息.所以这意味着我可以轻松地创建MVC的测试"视图"侧以及真正的"视图"侧,而无需更改控制器或模型中的任何内容.

另一种解释是在控制器和视图之间建立更紧密的耦合(它们实际上彼此依赖).这也意味着如果您决定从Swing UI视图切换到命令行视图,您可能还需要更改控制器.这更好/更糟......我认为这是个人品味的问题.

在我的原始答案中唯一不太好的是我在控制器部分中找到了业务逻辑,而Wiki页面清楚地表明它位于模型中.我可能对一些简单的Ruby on Rails实验感到困惑,其中模型只不过是数据访问层.我的错 ...