当前位置: 首页 > 知识库问答 >
问题:

java swing mvc架构- Q带MCVE的例子

杨经武
2023-03-14

如果有多个问题是极好的_informational_for_me-垃圾神的答案、那个、那个以及其他几个没有回答我问题的问题,
我们应该如何设计与ActionListeners位置相关的类
(以及整体MVC分离-下文将详细解释)。

  1. 问题解释
  2. 以我为例的文件结构树 (4)
  3. 编译/清理源的命令 (4)
  4. 来源

我已经读过关于MVC的文章,我想我理解了其中的大部分,为了这个问题,让我们假设这是真的。不谈细节:

  1. 根据控制器请求,从模型生成视图
    在大多数实现中,视图可以访问模型实例
  2. 控制器与用户交互,将更改传播到模型和视图
  3. 在极端简化中,模型是数据的容器
    可以通过视图进行观察

现在,我的困惑涉及 ActionListeners - 哪个类应该注册 - 并且还包含 - 按钮的代码,或者实际上是大多数 View 元素的代码,这些元素实际上不仅仅是指标,而是模型操纵器?

比方说,我们在视图中有两个项目——用于更改模型数据的按钮和一些仅用于更改视图外观的可视项目。让代码负责在视图类中改变视图外观似乎是合理的。我的问题与第一个案例有关。我有几个想法:

  • View创建按钮,因此在View中创建ActionListeners并同时注册回调是很自然的。但这需要View具有与模型相关的代码,打破封装。View应该对底层控制器或模型知之甚少,只能通过观察者与它交谈。
  • 我可以公开按钮等View项目并从Controller将ActionListeners附加到它们,但这再次破坏了封装。
  • 我可以为每个按钮实现一些回调-View会询问控制器是否有任何代码应该注册为给定按钮名称的ActionListener,但这似乎过于复杂,并且需要控制器和视图之间的名称同步。
  • 我可以假设,保持理智;),TableFactory中的按钮可能是公开的,允许将ActionListeners注入任何代码。
  • 控制器可以替换整个视图项(创建按钮并替换现有按钮),但这似乎很疯狂,因为它不是它的角色
.
└── test
    ├── controllers
    │   └── Controller.java
    ├── models
    │   └── Model.java
    ├── resources
    │   └── a.properties
    ├── Something.java
    └── views
        ├── TableFactory.java
        └── View.java

编译方式:

  • javac测试/Something.java测试/模型/*. java测试/控制器/*. java测试/视图/*. java

运行方式:

    < li>java测试。某事

清洁:

    < li >查找。-iname "*。class "-exec RM { } \;

此代码还包含国际化存根,为此我提出了单独的问题,这些行已明确标记,并且不应对答案产生任何影响。

package test.controllers;
import test.models.Model;
import test.views.View;
public class Controller {
// Stub - doing nothing for now.
}
package test.models;
import java.util.Observable;
public class Model extends Observable {
}
package test;

import test.views.View;
import test.models.Model;
import test.controllers.Controller;

public class Something {

    Model m;
    View v;
    Controller c;

    Something() {
        initModel();
        initView();
        initController();
    }

    private void initModel() {
        m = new Model();
    }

    private void initView() {
        v = new View(m);
    }
    private void initController() {
        c = new Controller(m, v);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                Something it = new Something();
            }
        });
    }
}
package test.views;

import java.awt.*;              // layouts
import javax.swing.*;           // JPanel

import java.util.Observer;      // MVC => model
import java.util.Observable;    // MVC => model
import test.models.Model;       // MVC => model

import test.views.TableFactory;

public class View {

    private JFrame root;
    private Model model;
    public JPanel root_panel;

    public View(Model model){
        root = new JFrame("some tests");
        root.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        root_panel = new JPanel();

        root_panel.add(new TableFactory(new String[]{"a", "b", "c"}));

        this.model = model;
        this.model.addObserver(new ModelObserver());

        root.add(root_panel);
        root.pack();
        root.setLocationRelativeTo(null);
        root.setVisible(true);
    }
}

class ModelObserver implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.print(arg.toString());
        System.out.print(o.toString());
    }
}
package test.views;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class TableFactory extends JPanel {

    private String[] cols;
    private String[] buttonNames;
    private Map<String, JButton> buttons;
    private JTable table;

    TableFactory(String[] cols){

        this.cols = cols;
        buttonNames = new String[]{"THIS", "ARE", "BUTTONS"};
        commonInit();
    }
    TableFactory(String[] cols, String[] buttons){

        this.cols = cols;
        this.buttonNames = buttons;
        commonInit();
    }

    private void commonInit(){

        this.buttons = makeButtonMap(buttonNames);
        DefaultTableModel model = new DefaultTableModel();
        this.table = new JTable(model);
        for (String col: this.cols)
            model.addColumn(col);

        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

        JPanel buttons_container = new JPanel(new GridLayout(1, 0));
        for (String name : buttonNames){
            buttons_container.add(buttons.get(name));
        }

        JScrollPane table_container = new JScrollPane(table);

        this.removeAll();
        this.add(buttons_container);
        this.add(table_container);
        this.repaint();
    }

    private Map<String, JButton> makeButtonMap(String[] cols){
        Map<String, JButton>  buttons = new HashMap<String, JButton>(cols.length);
        for (String name : cols){
            buttons.put(name, new JButton(name));
        }
        return buttons;
    }
}

编辑(回应下面的评论)

下一个信息来源

经过更多的思考,我理解了奥利维尔的评论,后来又理解了充满鳗鱼细节的气垫船......javax.swing.Action =

只有两件事让我担心(两者都非常罕见,但仍然如此):

    < li >由于我公开了视图添加动作的方法,实际上我相信任何人都只能从控制器或视图添加动作。但是如果模型可以访问视图,它也可以覆盖动作。 < li >也覆盖。如果我设置了某个对象的动作,然后忘记在不同的地方设置另一个动作,它就消失了,这可能会使调试变得困难。

共有3个答案

侯博易
2023-03-14

工作版本,压缩成一个Something.java文件。与< code > javac Something.java一起跑步

import java.util.*;
import java.util.Observer;
import java.util.Observable;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;

class Controller {
    private enum TYPE { RESET, ADD, DEL };
    private Model model;
    private View view;
    public Controller(Model model, View view){
        this.model = model;
        this.view = view;
        ((TableFactory) view.tf).setObjectAction(
            (Action) new ModelTouchingAction("reset*",TYPE.RESET),
            "BUTTON1" );
        ((TableFactory) view.tf).setObjectAction(
            (Action) new ModelTouchingAction("add*",TYPE.ADD),
            "BUTTON2" );
        ((TableFactory) view.tf).setObjectAction(
            (Action) new ModelTouchingAction("del*",TYPE.DEL),
            "BUTTON3" );
    }

    class ModelTouchingAction extends AbstractAction {
        private TYPE t;
        public ModelTouchingAction(String text, TYPE type) {
            super(text);
            this.t = type;
        }
        public void actionPerformed(ActionEvent e) {
            if(this.t == TYPE.ADD)
                model.add();
            else if(this.t == TYPE.DEL)
                model.del();
            else
                model.reset();
        }
    }
}

class Model extends Observable {
    private ArrayList<String[]> data;
    private static int cnt = 0;
    Model(){ reset(); }
    public void reset(){
        data = new ArrayList<String[]>();
        data.add(new String[]{"cell a1", "cell a2", "cell a3"});
        data.add(new String[]{"cell b1", "cell b2", "cell b3"});
        info();
    }
    public void add(){
        cnt++;
        data.add(new String[]{String.valueOf(cnt++), 
                 String.valueOf(cnt++), String.valueOf(cnt++)});
        info();
    }
    public void del(){
        if (data.size()>0){
            data.remove(data.size() - 1);
            info();
        }
    }
    private void info(){ setChanged();  notifyObservers(); }
    public ArrayList<String[]> get(){ return data; }
}

public class Something {

    Model m;
    View v;
    Controller c;
    Something() {
        initModel();
        initView();
        initController();
    }

    private void initModel() {
        m = new Model();
    }
    private void initView() {
        v = new View(m);
    }
    private void initController() {
        c = new Controller(m, v);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                Something it = new Something();
            }
        });
    }
}

class View {

    private JFrame root;
    private Model model;
    public JPanel root_panel;
    public TableFactory tf;

    public View(Model model){
        root = new JFrame("some tests");
        root.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        root_panel = new JPanel();
        tf = new TableFactory(new String[]{"col1", "col2", "col3"});
        root_panel.add(tf);

        this.model = model;
        this.model.addObserver(new ModelObserver(tf));

        root.add(root_panel);
        root.pack();
        root.setLocationRelativeTo(null);
        root.setVisible(true);
    }
}

class ModelObserver implements Observer {
    TableFactory tf;
    ModelObserver(TableFactory tf){ this.tf = tf; }
    @Override
    public void update(Observable o, Object arg) {
        if (null != o)
            this.tf.populate(((Model) o).get());
            // view reloads ALL from model, optimize it
            // check what to check to get CMD from Observable
        else
            System.out.print("\nobservable is null");
        if (null != arg)
            System.out.print(arg.toString());
        else
            System.out.print("\narg is null. No idea if it should be.");
    }
}

class TableFactory extends JPanel {

    private String[] cols;
    public String[] buttonNames;
    private Map<String, JButton> buttons;
    private JTable table;

    TableFactory(String[] cols){

        this.cols = cols;
        buttonNames = new String[]{"BUTTON1", "BUTTON2", "BUTTON3"};
        commonInit();
    }
    TableFactory(String[] cols, String[] buttons){

        this.cols = cols;
        this.buttonNames = buttons;
        commonInit();
    }

    private void commonInit(){

        this.buttons = makeButtonMap(buttonNames);
        DefaultTableModel tabModel = new DefaultTableModel();
        this.table = new JTable(tabModel);
        for (String col: this.cols)
            tabModel.addColumn(col);

        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

        JPanel buttons_container = new JPanel(new GridLayout(1, 0));
        for (String name : buttonNames){
            buttons_container.add(buttons.get(name));
        }

        JScrollPane table_container = new JScrollPane(table);

        this.removeAll();
        this.add(buttons_container);
        this.add(table_container);
        this.repaint();
    }
    public void populate(ArrayList<String[]> data){
        ((DefaultTableModel) table.getModel()).setRowCount(0);
        for(String[] row:data) addRow(row); 
    }
    private void addRow(String[] row){
        ((DefaultTableModel) table.getModel()).addRow(row);
        // this is actually called only by populate, model does not have single 
        // row update here (and onUpdate ModelObserver cannot differentiate 
        // yet what method to call on Observable, TODO: check CMD? )
    }
    private void delRow(int rowID){
        System.out.print("\nJPanel should be deleting table row " + rowID);
    }
    public void setObjectAction(Action action, String buttonName){
        buttons.get(buttonName).setAction(action);
    }
    private Map<String, JButton> makeButtonMap(String[] cols){
        Map<String, JButton>  buttons = new HashMap<String, JButton>(cols.length);
        for (String name : cols){
            buttons.put(name, new JButton(name));
        }
        return buttons;
    }
}
薛枫
2023-03-14

虽然@trashgod答案总结并在某种程度上扩展了评论的讨论,但我正在发布基于该讨论的丑陋(但有效)的解决方案(在控制器类中实现操作)

有什么需要改变的?

>

  • TableFactory-添加了public val setObjectAction(Action a, JButton b)并允许公众访问Map

    控制器类需要AbstractAction或从它继承的类的实现,以及将该类的对象设置为View的深度嵌套JComponent的代码。我发现这部分很难看,我不确定这种方法是否合理,或者是否有更好的解决方案(稍后会检查Hovercraft充满鳗鱼Beans)。

    没有额外的变化是必要的,但我修改了View.java一点点,以显示这个问题的目的稍微好一点,或在不同的光(这使View.java比MCVE,但IMHO描述的目的更好)

    package test.views;
    
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.table.DefaultTableModel;
    import javax.swing.Action;
    
    public class TableFactory extends JPanel {
    
        private String[] cols;
        private String[] buttonNames;
        public Map<String, JButton> buttons;
        private JTable table;
    
        TableFactory(String[] cols){
            this.cols = cols;
            buttonNames = new String[]{"some", "buttons"};
            commonInit();
        }
        TableFactory(String[] cols, String[] buttons){
    
            this.cols = cols;
            this.buttonNames = buttons;
            commonInit();
        }
    
        private void commonInit(){
    
            this.buttons = makeButtonMap(buttonNames);
    
            DefaultTableModel model = new DefaultTableModel();
            this.table = new JTable(model);
            for (String col: this.cols)
                model.addColumn(col);
    
            setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
    
            JPanel buttons_container = new JPanel(new GridLayout(1, 0));
            for (String name : buttonNames){
                buttons_container.add(buttons.get(name));
            }
    
            JScrollPane table_container = new JScrollPane(table);
    
            this.removeAll();
            this.add(buttons_container);
            this.add(table_container);
            this.repaint();
        }
    
        private Map<String, JButton> makeButtonMap(String[] cols){
            Map<String, JButton>  buttons = new HashMap<String, JButton>(cols.length);
            for (String name : cols){
                buttons.put(name, new JButton(name));
            }
            return buttons;
        }
    
        public void setObjectAction(Action a, JButton b){
            //it might be possible to set actions to something else than button, I imagine JComponent, but I havent figured out yet how
            b.setAction(a);
        }
    }
    
    package test.views;
    
    import java.awt.*;              // layouts
    import javax.swing.*;           // JPanel
    
    import java.util.Observer;      // MVC => model
    import java.util.Observable;    // MVC => model
    import test.models.Model;       // MVC => model
    
    import test.views.TableFactory;
    
    public class View {
    
        private JFrame root;
        private Model model;
        public JPanel root_panel;
        public JPanel some_views[];
    
        public View(Model model){
            root = new JFrame("some tests");
            root.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            root_panel = new JPanel();
    
            some_views = new JPanel[] { 
                           new TableFactory(new String[]{"a", "b", "c"}),
                           new TableFactory(new String[]{"e", "e"}) };
            JTabbedPane tabs = new JTabbedPane();
            for (JPanel tab: some_views){
                String name = tab.getClass().getSimpleName();
                tabs.addTab(name, null, tab, name);
                //tab.setObjectAction(action, (JComponent) button); // can set internal 'decorative' View's action here, that are not interacting with Model
                // for example, add new (empty) row to JTable, as this does not modify Model (yet)
            }
            root_panel.add(tabs);
    
    
            this.model = model;
            this.model.addObserver(new ModelObserver());
    
            root.add(root_panel);
            root.pack();
            root.setLocationRelativeTo(null);
            root.setVisible(true);
        }
    }
    
    class ModelObserver implements Observer {
    
        @Override
        public void update(Observable o, Object arg) {
            System.out.print(arg.toString());
            System.out.print(o.toString());
        }
    }
    
    package test.controllers;
    
    import test.models.Model;
    import test.views.View;
    
    
    import javax.swing.Action;
    import java.awt.event.*;
    import test.views.TableFactory;
    import javax.swing.*;
    
    public class Controller {
        private Model model;
        private View view;
        public Controller(Model model, View view){
            this.model = model;
            this.view = view;
    
            ((test.views.TableFactory)view.some_views[0]).setObjectAction(
                (Action) new ModelTouchingAction("move along, nothing here"),
                ((test.views.TableFactory)view.some_views[0]).buttons.get("FIRST") );
        }
    
        class ModelTouchingAction extends AbstractAction { 
            public ModelTouchingAction(String text) {
                super(text);
            }
            public void actionPerformed(ActionEvent e) {
                System.out.print("Invoked: " + e.toString());
            }
        }
    }
    

  • 赵嘉悦
    2023-03-14

    虽然模型-视图-控制器模式不是万灵药,但它是Swing应用程序设计中的一种常见模式。正如这里所提到的,Swing控件组件经常是视图包含层次结构的一部分:Swing应用程序的控制器除了将这些组件连接到相关的侦听器之外,可能没有什么事情要做;< code>Action的实例“可用于将功能和状态从组件中分离出来”,特别方便。在本例中,< kbd>Reset处理程序(一个简单的< code>ActionListener)嵌套在控制器中,但它也可以作为< code>Action从模型中导出。正如这里所建议的,你可能需要尝试不同的设计。这里列举了几种方法。

     类似资料:
    • 我正在探索新的Android架构组件,并希望将其实现到一个Android应用程序中。当我在iOS开发中使用MVVM时,我对MVVM非常了解。阅读了Google提供的Android Architecture Components guide:https://developer.Android.com/topic/libraries/Architecture/guide.html 我有几个问题...

    • 本文向大家介绍asp.net实现三层架构的例子,包括了asp.net实现三层架构的例子的使用技巧和注意事项,需要的朋友参考一下 看了2天的三层架构,其实自己以前也看过这个,可以由于没有使用过,所以对于三层架构也只是知道罢了,昨天看了一下他一些内部的具体架构,三层分别是BLL、WEB、DAL,在web接受用户请求,bll处理业务,dal处理数据库事务,下面是一个简单的例子!这是一个添加新员工的页面:

    • 我正在玩GraphQL和Spring Boot,但我在这个问题上停留了一段时间。 首先,这是我的build.gradle: 我有一个实体,比如说-Dog,一个存储库,一个服务,一个Mutator和一个查询。在/resources中,我有。 但由于某些原因,我无法启动应用程序。错误消息如下:。当我删除对它启动,但找不到模式。 有什么想法吗?

    • 这对我来说是棘手的部分。我想实现的逻辑,但对我来说,在应用程序层实现这一点没有意义,因为持久性上下文在基础结构层(例如或)。但是,如果我正确理解六边形结构,我就不能直接在基础结构层实现接口,因为基础结构层不应该知道域层。 我错过了什么?

    • 问题内容: 它被认为是很好的做法,对于应用程序的每个层(即一个例外,,等)。但是,如果我的服务层直接调用DAO方法(持久层方法)而不进行其他操作,该怎么办? 像这样: 我是否应该将此DAO方法调用包装为一个块,然后将可能的异常抛出为?每个DAO方法应该只抛出吗? 问题答案: 那么,您的Dao异常与服务层无关,并且服务层与dao层异常无关。正确的方法是捕获dao异常,然后将新的自定义异常扔到服务层。

    • 为什么要有Ansbile Tower Ansilbe Tower一款针对企业级的收费软件。 在上一节的Ansible架构中和下一章Ansbile的安装中会讲到,每一台被ansible远程管理的主机,都需要配置基于key的ssh连接。个人用户自己管理几台虚拟机和远程主机不会有什么问题,但是作为企业级也用户,则满足不了业务和安全上的需求。 首先,每增加一台主机,都需要手工配置一下ssh连接,企业级的p