命令模式使用JComponents时的有用性

时间:2014-02-02 15:55:48

标签: swing design-patterns jbutton jmenuitem command-pattern

所以,我正在使用Swing库开发一个程序,我显然有按钮和菜单项。其中一些应该做同样的事情,我认为使用命令模式应该是这样做的方式,例如,我有一个“保存”按钮和一个“保存”菜单项,他们必须实现相同的保存算法。 命令模式似乎没问题,但我无法确定谁是接收器。是不是一个命令应该在一个实现某种“接收器接口”的对象上工作,这样你就可以在不同的接收器上使用不同的命令来耦合它们?看起来我的模式实现中没有“接收器”。 我的另一个疑问是命令是否应该作为单例实现,因为你可以从同一个项目的不同部分调用它的函数,并且只需要实例化一次并使其静态可调用就可以了吗?

谢谢。

2 个答案:

答案 0 :(得分:4)

  

“我显然有按钮和菜单项。其中一些应该做同样的事情,”

正如@nIcEcOw所说,这就是Action的用途。 This Answer正好显示了这一点。

How to use Actions中所述:

  

Action可用于将功能和状态与组件分开。例如,如果您有两个或多个执行相同功能的组件,请考虑使用Action对象来实现该功能。 Action对象是一个动作侦听器,它不仅提供动作事件处理,还集中处理动作事件触发组件的状态,例如工具栏按钮,菜单项,公共按钮和文本字段。操作可以处理的状态包括文本,图标,助记符,已启用和已选择状态。

enter image description here

An只有三个Actions。 Ont打开,保存和新建。每个Action都有一个ActionCommandicon,以及要执行的操作。 JMenuItemJToolBar按钮共享相同的Action并执行相同的操作。这是您可以运行的代码。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

public class ActionTest {

    public ActionTest() {
        ImageIcon openIcon = new ImageIcon(
                ActionTest.class.getResource("/resources/image/open.gif"));
        ImageIcon saveIcon = new ImageIcon(
                ActionTest.class.getResource("/resources/image/save.gif"));
        ImageIcon newIcon = new ImageIcon(
                ActionTest.class.getResource("/resources/image/new.gif"));

        Action openAction = new AbstractAction("Open", openIcon) {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Open File");
            }
        };
        Action saveAction = new AbstractAction("Save", saveIcon) {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Save File");
            }
        };
        Action newAction = new AbstractAction("New", newIcon) {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("New File");
            }
        };

        JMenuItem openMenuItem = new JMenuItem(openAction);
        JMenuItem saveMenuItem = new JMenuItem(saveAction);
        JMenuItem newMenuItem = new JMenuItem(newAction);

        JMenuBar menuBar = new JMenuBar();
        JMenu fileMenu = new JMenu("File");
        fileMenu.add(openMenuItem);
        fileMenu.add(saveMenuItem);
        fileMenu.add(newMenuItem);
        menuBar.add(fileMenu);

        JToolBar toolBar = new JToolBar();
        toolBar.add(Box.createHorizontalGlue());
        toolBar.setBorder(new LineBorder(Color.LIGHT_GRAY, 1));
        toolBar.add(newAction);
        toolBar.add(openAction);
        toolBar.add(saveAction);

        JFrame frame = new JFrame("Toolbar and Menu Test");
        frame.setJMenuBar(menuBar);
        frame.add(toolBar, BorderLayout.PAGE_START);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.setVisible(true);

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new ActionTest();
            }
        });
    }
}

正如上面提到的教程的引用中所述,除了向Action添加图像和动作命令之外,您还可以做更多的事情。您可以使用它来设置助记符和加速器。这是一个自定义Action类,需要

  1. 动作命令String
  2. 一个图标
  3. 工具提示的说明
  4. 助记符
  5. 和一个关键的加速器。

    private class MyAction extends AbstractAction {
    
        String name;
    
        public MyAction(String name, Icon icon) {
            super(name, icon);
            this.name = name;
        }
    
        public MyAction(String name, Icon icon, String desc,
                        Integer mnemonic, KeyStroke accelorator) {
            super(name, icon);
            putValue(Action.SHORT_DESCRIPTION, desc);
            putValue(Action.MNEMONIC_KEY, mnemonic);
            putValue(Action.ACCELERATOR_KEY, accelorator);
            this.name = name;
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            switch (name) {
                case "Open":
                    System.out.println("Open");
                    break;
                case "New":
                    System.out.println("New");
                    break;
                case "Save":
                    System.out.println("Save");
                    break;
            }
        }
    }
    
  6. 以下是此Action

    的实例化
    Action newAction = new MyAction("New", newIcon,
                "Creates a new file",
                new Integer(KeyEvent.VK_N),
                KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
    

    这是新结果。您将在菜单中看到actionCommand,以及关键助记符和加速器,工具提示,您将看到jtoolbar按钮共享相同的特征。您还将在最终代码中看到,一旦创建了组件,您就不会再这样做了。您所做的只是将Action添加到JToolBarJMenu,让他们发挥他们的魔力。

    enter image description here

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    
    public class ActionInterfaceDemo extends JFrame {
    
        public ActionInterfaceDemo() {
            ImageIcon openIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/open.gif"));
            ImageIcon saveIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/save.gif"));
            ImageIcon newIcon = new ImageIcon(ActionInterfaceDemo.class.getResource("/resources/image/new.gif"));
    
            Action openAction = new MyAction("Open", openIcon,
                    "Opens a file",
                    new Integer(KeyEvent.VK_O),
                    KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
            Action saveAction = new MyAction("Save", saveIcon,
                    "Saves a file",
                    new Integer(KeyEvent.VK_S),
                    KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
            Action newAction = new MyAction("New", newIcon,
                    "Creates a new file",
                    new Integer(KeyEvent.VK_N),
                    KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
    
            JMenuBar menuBar = new JMenuBar();
            JMenu fileMenu = new JMenu("File");
            setJMenuBar(menuBar);
            menuBar.add(fileMenu);
    
            fileMenu.add(newAction);
            fileMenu.add(openAction);
            fileMenu.add(saveAction);
    
    
            JToolBar toolBar = new JToolBar("Alignment");
            toolBar.setBorder(BorderFactory.createLineBorder(Color.BLUE));
            toolBar.add(Box.createHorizontalGlue());
            toolBar.add(newAction);
            toolBar.add(openAction);
            toolBar.add(saveAction);
    
            add(toolBar, BorderLayout.PAGE_START);
            add(new JScrollPane(new TextArea(10, 40)), BorderLayout.CENTER);
    
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            setTitle("Action Interface Demo");
            pack();
            setLocationByPlatform(true);
            setVisible(true);
        }
    
        private class MyAction extends AbstractAction {
    
            String name;
    
            public MyAction(String name, Icon icon) {
                super(name, icon);
                this.name = name;
            }
    
            public MyAction(String name, Icon icon, String desc,
                    Integer mnemonic, KeyStroke accelorator) {
                super(name, icon);
                putValue(Action.SHORT_DESCRIPTION, desc);
                putValue(Action.MNEMONIC_KEY, mnemonic);
                putValue(Action.ACCELERATOR_KEY, accelorator);
                this.name = name;
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                switch (name) {
                    case "Open":
                        System.out.println("Open");
                        break;
                    case "New":
                        System.out.println("New");
                        break;
                    case "Save":
                        System.out.println("Save");
                        break;
                }
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable(){
                public void run() {
                    new ActionInterfaceDemo();
                }
            });
        }
    }
    

    更新

    更好地解释Action命令模式

    的关系

    Command Pattern

    所述
      

    命令模式是一种常用模式,它将方法调用或类似动作的代码封装到单个类中。当您为单个操作设置多个调用者时(例如,按钮和菜单项可以执行相同的操作),能够将一个或多个方法打包到类中的优势变得明显。

         

    在Swing和Borland Delphi编程中,Action是一个命令对象。除了能够执行所需命令外,Action还可以具有关联的图标,键盘快捷键,工具提示文本等。可以仅使用Action对象完全初始化工具栏按钮或菜单项组件。

    所以基本上Swing通过使用Actions

    使用命令模式的概念

    关于OP的问题

      

    “命令模式似乎没问题,但我无法确定接收者是谁。”

    对于接收器,维基使用文本编辑器作为示例,并将接收器定义为

      

    接收器,目标对象:即将复制,粘贴,移动等对象。接收器对象拥有由命令的execute方法调用的方法。接收器通常也是目标对象。例如,如果接收者对象是游标并且该方法被称为moveUp,则可以预期游标是moveUp动作的目标。另一方面,如果代码由命令对象本身定义,则目标对象将完全是另一个对象。

    命令模式的主要组成部分如下所述

    与命令模式始终相关的四个术语是命令,接收者,调用者和客户端。

      

    客户端,来源,推荐人:点击按钮,工具栏按钮或菜单项,用户按下快捷键。

    所以要把它们放在一起:

    1. MenuItem(客户端)调用它
    2. 操作(命令对象),后者将其调用actionPerformed
    3. 接收器上调用方法。
    4. 使用Java示例

      可以很好地阅读wiki article

答案 1 :(得分:2)

当两个或多个组件的意思完全相同时,应该查看Action,这会减少重复的代码。

进一步帮助的小例子:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ActionExample {

    private JFrame frame;
    private JButton button;
    private JMenuItem exitItem;
    private Action commonActions;

    private class CommonActions extends AbstractAction {

        public CommonActions(String title, String desc) {
            super(title);
            putValue(SHORT_DESCRIPTION, desc);
        }

        @Override
        public void actionPerformed(ActionEvent ae) {
            JOptionPane.showMessageDialog(frame,
                "Closing Frame", "Information", JOptionPane.INFORMATION_MESSAGE);
            frame.dispose();
        }
    };

    private void displayGUI() {
        frame = new JFrame("Action Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);                

        commonActions = new CommonActions("Exit", "To Exit the Application");

        JPanel contentPane = new JPanel();
        button = new JButton();
        button.setAction(commonActions);
        contentPane.add(button);

        frame.setJMenuBar(getMenuBar());
        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JMenuBar getMenuBar() {
        JMenuBar menuBar = new JMenuBar();
        JMenu fileMenu = new JMenu("File");

        exitItem = new JMenuItem(commonActions);
        fileMenu.add(exitItem);
        menuBar.add(fileMenu);

        return menuBar;
    }

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                new ActionExample().displayGUI();
            }
        };
        EventQueue.invokeLater(runnable);
    }
}

添加了SINGLETON PATTERN的示例(虽然我不确定这种方法(关于这种方法有多好))

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ActionExample {

    private JFrame frame;
    private JButton button;
    private JMenuItem exitItem;

    private void displayGUI() {
        frame = new JFrame("Action Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        CommonActions.setValues(frame);

        JPanel contentPane = new JPanel();
        button = new JButton();
        button.setAction(CommonActions.getInstance());
        contentPane.add(button);

        frame.setJMenuBar(getMenuBar());
        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JMenuBar getMenuBar() {
        JMenuBar menuBar = new JMenuBar();
        JMenu fileMenu = new JMenu("File");

        exitItem = new JMenuItem(CommonActions.getInstance());
        fileMenu.add(exitItem);
        menuBar.add(fileMenu);

        return menuBar;
    }

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                new ActionExample().displayGUI();
            }
        };
        EventQueue.invokeLater(runnable);
    }
}

class CommonActions extends AbstractAction {

    private static CommonActions commonActions = null;
    private static JFrame frame = null;

    static {
        try {
            commonActions = new CommonActions("Exit", "To Exit the Application");
        } catch (Exception e) {
            throw new RuntimeException("BINGO, an error");
        }
    }

    private CommonActions(String title, String desc) {
        super(title);
        putValue(SHORT_DESCRIPTION, desc);
    }

    public static CommonActions getInstance() {
        return commonActions;
    }

    public static void setValues(JFrame f) {
        frame = f;
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
        JOptionPane.showMessageDialog(frame,
            "Closing Frame", "Information", JOptionPane.INFORMATION_MESSAGE);
        frame.dispose();
    }
}