使用 JComponents 时命令模式的用处

use*_*545 4 swing design-patterns jbutton command-pattern jmenuitem

所以,我正在使用 Swing 库开发一个程序,我显然有按钮和菜单项。其中一些应该做同样的事情,我认为使用命令模式应该是这样做的方式,例如我有一个“保存”按钮和一个“保存”菜单项,他们必须实现相同的保存算法. 命令模式似乎没问题,但我不知道谁是接收者。难道一个命令不应该在一个实现某种“接收器接口”的对象上工作,这样你就可以在不同的接收器上使用不同的命令来任意耦合它们吗?看起来我的模式实现中没有“接收器”。我的另一个疑问是是否应该将命令实现为单例,

谢谢你。

Pau*_*tha 5

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

正如@nIcEcOw 所指出的,这就是Actions 的用途。这个答案恰恰说明了这一点。

如何使用操作中所述

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

在此处输入图片说明

安只有三个Actions。打开、保存和新建。每个Action都有要执行的ActionCommand, 和icon, 和 操作。无论是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();
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

正如上述教程中的引用所述,您可以做的不仅仅是将图像和操作命令添加到Action. 您可以使用它来设置助记符和加速器。这是一个自定义Action类,需要

  1. 一个动作命令字符串
  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;
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

这是这个的实例化 Action

Action newAction = new MyAction("New", newIcon,
            "Creates a new file",
            new Integer(KeyEvent.VK_N),
            KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
Run Code Online (Sandbox Code Playgroud)

这是新的结果。您将actionCommand在菜单中看到,以及关键助记符和加速器、工具提示,并且您将看到 jtoolbar 按钮具有相同的特征。您还将在最终代码中看到,一旦创建了组件,就不会出现一次。所有你要做的是添加ActionJToolBarJMenu,让他们创造了神奇。

在此处输入图片说明

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();
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

更新

更好地解释Action命令模式的关系

命令模式中所述

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

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

所以基本上 Swing通过使用命令模式的概念Actions

至于OP的问题

“命令模式似乎没问题,但我不知道谁是接收者。”

至于接收器,wiki 使用文本编辑器作为示例,并将接收器定义为这样

Receiver, Target Object:即将被复制、粘贴、移动等的对象。接收者对象拥有命令的execute方法调用的方法。接收器通常也是目标对象。例如,如果接收者对象是一个游标并且该方法被称为 moveUp,那么人们会期望游标是 moveUp 操作的目标。另一方面,如果代码是由命令对象本身定义的,则目标对象将是一个完全不同的对象。

命令模式的主要组件如下所述

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

Client、Source、Invoker:被点击的按钮、工具栏按钮或菜单项,用户按下的快捷键。

所以把它们放在一起:

  1. MenuItem(客户端)调用它
  2. 调用它的动作(命令对象actionPerformed依次调用它
  3. 接收器上调用一个方法。

wiki文章是一个Java的例子很好看