Java中事件上的MVC模式启用按钮

Java中事件上的MVC模式启用按钮,java,model-view-controller,Java,Model View Controller,我现在正开始使用MVC模式,我正在思考一些事情。设想以下情况: 很明显,我看到了。在此视图中,用户可以选择一个文件,然后单击加载按钮。单击此加载按钮后,应立即启用我的gui中的所有其他组件,因为只要未加载任何文件,它们就被设置为禁用 我到目前为止所做的: 我已根据以下方案创建了一个视图: public class GUI implements Observer { private JButton loadButton, showButton; private JComboBox name

我现在正开始使用MVC模式,我正在思考一些事情。设想以下情况:

很明显,我看到了。在此视图中,用户可以选择一个文件,然后单击加载按钮。单击此加载按钮后,应立即启用我的gui中的所有其他组件,因为只要未加载任何文件,它们就被设置为禁用

我到目前为止所做的:

我已根据以下方案创建了一个视图:

public class GUI implements Observer {
  private JButton loadButton, showButton;
  private JComboBox nameBox;
  private Controller controller = new Controller();

  // Initialising Component and so on...
  loadButton.addActionListener(... // calls the loadFile()-Method

  public void update(Observable arg0, Object arg1) {
    // What to do here?

  }
}
还有这样一个控制器:

public class Controller extends Observable {
  public void loadFile() {
    // Load selected File
    notifyObservers(); // <--- What should they be notified about in order to enable their component?
  }
}

因此,我的问题是,我不确定在控制器上启用组件是否明智。还是最好在视图中检查文件是否已加载,然后将组件设置为已启用?

控制器是控制视图的层。所以,如果你有一些规则,比如禁用按钮,控制器必须知道它

您必须遵守为按钮公开禁用/启用方法的约定

您的控制器必须根据您的规则调用此方法。您的GUI必须发送事件并由控制器进行操作


如果您这样做,您的GUI和控制器将是模块化的。当您必须将GUI框架开关更改为light client时,请使用另一个库。。。您的控制器将不会更改。当然,您不必实现任何规则,因为只有控制器知道这些规则。

您不显示您的模型,但它可能知道文件是否已成功加载

所以我只想告诉大家模型已经改变了

然后视图有责任决定显示什么、启用什么按钮或其他。所以它可能有这样的逻辑

if ( model.isFileLoaded() ){
     enable buttons
} else {
      disable all buttons except the load
      if ( model.hadError() ) {
          display helpful message (and let user specify a different file)
      }
}

通过这种方式,控制器不需要知道视图对模型中的哪些特定部分感兴趣,也不需要知道关于按钮和视图状态的任何信息。

我可能会这样做,即关闭模型更改的所有内容。一些可观察的bean将保存该文件,而您的“控制器层”将由bean的侦听器组成,这些侦听器根据文件属性值的存在或不存在来更新按钮状态

这将使一切解耦,因为您可以轻松地添加其他按钮、删除按钮等,而无需更改代码。如果您有其他设置或清除文件属性的方法,则不需要复制并粘贴所有禁用/启用代码

因此,在上面的示例中,loadFile将加载该文件并将其设置在模型bean上。该值更改将导致在更新按钮状态的侦听器上触发PropertyChangeEvents

从我的角度来看:在直接设置按钮的基础上操作操作的额外积分。这些操作甚至可以是bean更改侦听器,只需调用setEnabled即可。但我喜欢尽可能少地将控制器代码与实际的swing组件耦合

如果这令人困惑,我可以提供示例代码好的,提供的代码

所以,我的纯方法是让所有按钮都有动作支持。。。甚至可以使用公共基类:

public abstract class FileRequiredAction extends AbstractAction 
                                implements PropertyChangeListener {
    public FileRequiredAction( String name ) {
        super( name );
    }

    public void propertyChanged( PropertyChangeEvent event ) {
        if ("file".equals( event.getName())
            setEnabled( event.getNewValue() != null );
    }
}
然后假设您有一个适当的模型bean,该bean带有一个触发正确事件的setFile方法,您的UI设置代码如下所示:

Action a;
a = new FileRequiredAction( "Show" ) {
        public void actionPerformed( ActionEvent event ) {
            // Do the showing
        }
    };
myBean.addPropertyChangeListener( "file", a );
JButton showButton = new JButton( a );

...repeat for other actions that require "file".
加载按钮需要做的就是调用myBean.setFile someNonNullValue,按钮将自动启用。额外的好处是,其他一些操作会清除它们再次被禁用的值

对于完整的代码,我省略了一些事情,比如在myBean.getFile已经有值的情况下确保操作初始化其启用状态,等等


希望这能让事情变得更清楚。

我不会把这些架构的东西弄得太复杂。。。从我的角度来看,我认为这个案例中,您的模型只是文件及其内容。你的观点就是你的观点;-控制器是按钮/动作监听器。只要做:

class MyViewClass extends SomeSwingComponent
{

}


只有当用户在加载文件时可以执行任何操作时,您才应该考虑在观察线程中加载文件。

不使用Observer类,我可以想到的一种方法如下:

公共类GUI{ 私有JButton loadButton、showButton; 专用JComboBox名称框;

loadButton.addActionListener(new Controller(GUI));  

public void enableShow(boolean b) {  
    // enable Show button if b is true  
}  

public void enableName(boolean b) {  
    // display Name combo box if b is true  
}  
}

公共类控制器实现ActionListener{ 私有GUI视图

public Controller(GUI v) {  
    this.view = v;  
}  

public void actionPerformed(ActionEvent pActionEvt) {  
    boolean status = loadFile();  
    if (status) {  
        view.enableShow(true);  
        view.enableName(true);  
    }
}  

private boolean loadFile(String file) {
    // loads the specified file and return true if load is successful  
}  
}

Swing框架负责在控制器中调用actionPerformed方法 当用户单击加载按钮时

您还可以将loadFile方法放在一个单独的类中 将作为一个榜样

控制器分段的一个优点是您可以 使用模拟框架独立地对控制器进行单元测试。 或者,您可以创建一个GUI类来实现一个接口,然后使用
依赖注入以单元测试控制器类中的逻辑

这会将视图的详细信息耦合到控制器逻辑中。没必要那样
.1:同意-一旦你从模型的角度看问题,它就会变得非常明显。
public Controller(GUI v) {  
    this.view = v;  
}  

public void actionPerformed(ActionEvent pActionEvt) {  
    boolean status = loadFile();  
    if (status) {  
        view.enableShow(true);  
        view.enableName(true);  
    }
}  

private boolean loadFile(String file) {
    // loads the specified file and return true if load is successful  
}