java - 如何更新 MVC 模式程序中的表

标签 java multithreading model-view-controller user-interface observer-pattern

我使用 MVC 架构版本创建了一个程序。该代码的目的是抓取网页列表的 h1 标题并将结果返回到 JTable。

到目前为止,我的程序运行良好。它按照我想要的方式返回结果,但直到最后才更新表。我希望它在结果出现时更新表格。我希望以一种在我刚刚学习时考虑最佳实践原则的方式来执行此操作。

我想按照我想要的方式更新它,我将不得不稍微更改我的代码。我不确定动态更新 GUI 的最佳方法(线程、观察者,还是其他东西?)。我什至不确定问题“这段代码应该放在我的 MVC 模式中的哪个位置?”有道理吗?

无论如何,这是我的观点:

public class SearchView extends JFrame{
//Components
private JLabel selectElementLabel = new JLabel("Element Selector:");
private JTextField selectElement = new JTextField("h1");;
private JComboBox<String> selectLocale; 

private DefaultTableModel tableModel = new DefaultTableModel();
private JTable resultTable = new JTable(tableModel);

private JLabel statusLabel;
private JButton runButton = new JButton("Run");
private JButton clearButton = new JButton("Clear");

private SearchModel s_model;

//Constructor
public SearchView(SearchModel model) {
    //Set the Logic here(model)
    s_model = model;    

    //Initialise Components here(model)
    selectLocale = new JComboBox<>(s_model.getLocales());
    selectLocale.setSelectedIndex(13);

    //Layout Components
    JPanel userInputPanel = new JPanel();
    userInputPanel.setLayout(new BoxLayout(userInputPanel, BoxLayout.X_AXIS));
    userInputPanel.add(selectElementLabel);
    userInputPanel.add(selectElement);
    userInputPanel.add(selectLocale);

    tableModel.addColumn("Page");
    tableModel.addColumn("Data");
    resultTable.setFillsViewportHeight(true);

    JScrollPane resultScroller = new JScrollPane(resultTable);
    resultScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    resultScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    resultScroller.setAlignmentX(Component.LEFT_ALIGNMENT);

    JPanel controlButtons = new JPanel();
    controlButtons.setLayout(new FlowLayout(FlowLayout.RIGHT));
    controlButtons.add(statusLabel = new JLabel(s_model.getState()));
    controlButtons.add(clearButton);
    controlButtons.add(runButton);


    this.setTitle("Element Searcher");
    this.add(BorderLayout.NORTH, userInputPanel);
    this.add(BorderLayout.CENTER, resultScroller);
    this.add(BorderLayout.SOUTH, controlButtons);
    this.setExtendedState(Frame.MAXIMIZED_BOTH); 
    this.setMinimumSize(new Dimension(900, 600));
    this.setVisible(true);  
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        
}

void reset(){
    tableModel.setRowCount(0);
}

String getSelectedElement(){
    return selectElement.getText();
}

String getSelectedLocale(){
    return selectLocale.getSelectedItem().toString();
}

void setResults(Object[] result){
    tableModel.addRow(result);
}

void addRunListener(ActionListener run){
    runButton.addActionListener(run);
}

void addClearListerner(ActionListener clear){
    clearButton.addActionListener(clear);
}

}

Controller :

public class SearchController {

private SearchModel s_model;
private SearchView s_view;

public SearchController(SearchModel model, SearchView view) {
    s_model = model;
    s_view = view;

    s_view.addRunListener(new RunListener());
    s_view.addClearListerner(new ClearListener());

}

class RunListener implements ActionListener{
    public void actionPerformed(ActionEvent e){
        String selectedLocale = null;
        try {
            selectedLocale = s_view.getSelectedLocale();
            s_model.setPageList(selectedLocale);
            for (String pageUrl : s_model.getPageList()){
                s_view.setResults(s_model.getResults(pageUrl));
            }
        } catch (Exception e1) {
            System.out.println(e1);
        }
    }
}

class ClearListener implements ActionListener{
    public void actionPerformed(ActionEvent e){
        s_model.reset();
        s_view.reset();
    }
}

}

最后是我的模型:

public class SearchModel {
//Constants
private static final String[] localeStrings = { "cs-cz", "da-dk", "de-at", "de-ch", "de-de", "el-gr", "en-ae", "en-au", "en-ca", "en-gb", "en-ie", "en-in", "en-nz", "en-us", "en-za", "es-cl", "es-co", "es-es", "es-mx", "fi-fi", "fr-be", "fr-ca", "fr-ch", "fr-fr", "hu-hu", "it-it", "ja-jp", "ko-kr", "nb-no", "nl-be", "nl-nl", "pl-pl", "pt-br", "pt-pt", "ru-ru", "sk-sk", "sv-se", "zh-hk", "zh-sg", "zh-tw" };
private static final String INITIAL_STATE = "idle";
private HashSet<String> pageList;
private Object[] scrapeResult;
private String locale = "en-us";

//Search State
private String searchState;

public SearchModel() {
    reset();
}

public void setPageList(String loc){
    locale = loc;
    ScrapeXML scraper = new ScrapeXML(locale);
    pageList = scraper.getUrls();
}

public void setResults(String page){
    ScrapeElements scraper = new ScrapeElements(page, locale);
    scrapeResult = scraper.getResults();
}

public void reset(){
    searchState = INITIAL_STATE;
}

public String[] getLocales(){
    return localeStrings;
}

public String getState(){
    return searchState;
}

public HashSet<String> getPageList(){
    return pageList;
}

public Object[] getResults(String page){
    setResults(page);
    return scrapeResult;
}

}

如果您对代码本身有任何意见或建议请告诉我!

谢谢!

最佳答案

如果没有双重调度(也称为监听器模式),您就无法真正使其成为 MVC。对于模型,您需要添加

 public void addListener(ModelListener listener) {
 }

并且(这样当“你的”模型发生变化时,你就可以停止收听旧模型)

 public void removeListener(ModelListener listener) {
 }

因此您可以让几乎未知的对象添加自身来接收模型更新,这些更新通常通过“Listener”接口(interface)传递。

 public interface ModelListener {

   public void modelChanged(ModelChangeEvent event);

 }

其中 ModelChangeEvent 通常类似于

 public class ModelChangeEvent {

   private Model source;

   public ModelChangeEvent(Model source, <possibly other fields here>) {
     this.source = source;
   }

   public Model getSource() {
     return source;
   }
 }

有了这个,听众就可以像这样行事

 public ModelView implements ModelListener {

  ... stuff ...

   public void modelChanged(ModelChangeEvent event) {
     if (event.getSource() == model) {
       // this is my model!
       name = model.getName();
       age = model.getAge();
       ... and any other updates that this listener cares about ...
     }
   }

监听器在更改处理程序中的操作方式具有一定的灵 active ,最重要的部分是监听器的范围包含灵 active ,模型现在基本上可以忽略正在监听的内容,而只关注正在监听

  ... in the model class ...

  private void notifyListeners() {
    ModelChangeEvent event = new ModelChangeEvent(this);
    for (ModelListener listener : listeners) {
      listener.modelChanged(event);
    }
  }

同样,通知监听器的方式有很大程度的灵 active ,但关键元素是当模型的可观察“元素”发生变化时,所有监听对象都应该接收调用。它通常放在私有(private)方法中的原因是因为它使重用模型变得更加容易,就像这样

  ... in the model class ...
  public void setName(String name) {
    this.name = name;
    notifyListeners();
  }

在每个听力类(class)中,它可能(也可能不会)读出名称,具体取决于它所显示的内容。

  ... in a name and age sensitive listening class ...
   public void modelChanged(ModelChangeEvent event) {
     if (event.getSource() == model) {
       name = model.getName();
       age = model.getAge();
     }
   }


  ... in a name insensitive listening class ...
   public void modelChanged(ModelChangeEvent event) {
     if (event.getSource() == model) {
       // this is my model!
       age = model.getAge();
     }
   }

一旦你有了这样的东西,你的 View 应该监听你的模型,因此 Controller 不必对模型的可观察“元素”进行某种轮询,并定期刷新 View 的感兴趣的表示项。

是的,这意味着您的 View 通常拥有模型中数据的副本,这是一件好事;因为,如果您决定需要修改演示文稿,则您有一个要修改的数据副本。在“名称显示” View 中,您可能希望强制使用一致的大写,如下所示

  ... in a name and age sensitive listening class ...
  ... note that _name_ is the name in the _view_ not the model! ...
   public void modelChanged(ModelChangeEvent event) {
     if (event.getSource() == model) {
       name = capitalize(model.getName());
       age = model.getAge();
     }
   }

现在您的模型和 View 将始终保持同步。这极大地简化了 Controller 代码,现在它只需要处理命令。对于每个命令,它可能会执行以下一项(或多项)操作:

  1. 调用模型中的方法( View 将自动更新)。
  2. 查找并切换 View 的模型(就像“下一个客户”的按钮,不要破坏 View ,只需设置 View 的模型)。这需要在 View 中编写 setModel(Model) 方法。
  3. 创建或销毁 View (可能设置其模型)。

关于java - 如何更新 MVC 模式程序中的表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15574119/

相关文章:

Java 内存模型——在跨越内存屏障时究竟将什么刷新到内存中?

Java 程序在线程完成之前退出。如何让 Cucumber-JVM 等待线程退出?

java - jSTL迭代器映射和列表

java - 使用@TypeConverter的ConcurrentModificationException

java - 使用 REST 上传多个文件

ios - 复杂的敌人行为和模式

model-view-controller - 报告生成逻辑在 MVC 结构中应该放在哪里?

java - Oracle MAF 的 validateDateTimeRange 标记

c# - 多线程与顺序处理的效果如何?

asp.net-mvc-4 - Kendo UI 网格批量编辑需要显示 bool 值 true(复选框需要选中) false(复选框需要取消选中)