Gof设计模式中介者模式The quick brown box实现详解

动机

如图,这是Gof《设计模式》一书第五章中Mediator中介者模式动机一节的例子。

原文:

考虑一个图形用户中对话框的实现。对话框使用一窗口来展现一系列的窗口组件,如按钮、菜单和输入域等。如下图。

在这里插入图片描述

实现效果

这是我的实现效果。

点击font choose按钮,弹出对话框界面。

在这里插入图片描述

选择效果后,点击确定。

在这里插入图片描述

文本区域的字体已改变。

在这里插入图片描述

完整代码

简要思路:

我们自定义两个类 FontChooserFontChooserDialog

FontChooserDialog 继承自 JDialog,自定义对话框组件。

FontChooser 继承于 JFrame,自定义 JFrame 容器。

并由 FontChooser 包含一个 FontChooserDialog ,由此实现以上效果。

FontChooserDialog类

package behaviorTypePattern.mediatorPattern.GUI;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 自定义对话框
 */
public class FontChooserDialog extends JDialog {

    //组件
    private JTextField inputText;
    private JList fontList;
    private Button cancelButton;
    private Button okButton;
    private JRadioButton mediumButton;
    private JRadioButton boldButton;
    private JRadioButton romanButton;
    private JRadioButton italicButton;
    private JSlider sizeSlider;

    //构成Font的参数值
    private String name;
    private int weight;
    private int slant;
    private int fontSize;


    public FontChooserDialog(Frame parent, String title) {
        //在构造函数完成自定义对话框的绘制
        super(parent, title);

        //采用盒子布局
        Box overallBox = Box.createVerticalBox();   //负责整体布局的盒子
        Box familyBox = Box.createHorizontalBox();  //负责family区域布局的盒子
        Box fontListBox = Box.createHorizontalBox();//负责列表布局的盒子
        Box weightBox = Box.createHorizontalBox();//负责weight区域布局的盒子
        Box slantBox = Box.createHorizontalBox();//负责slant区域布局的盒子
        Box buttonBox = Box.createHorizontalBox();//负责按钮布局的盒子

        //输入框 创建并绑定对应盒子
        inputText = new JTextField(10);
        inputText.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setName(inputText.getText());//当在inputText区域按回车时把文本赋值给name
            }
        });
        familyBox.add(new Label("Family"));
        familyBox.add(inputText);

        //列表 创建并绑定对应盒子
        String[] items = new String[]{"宋体","楷体"};
        fontList = new JList(items);
        fontList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        fontList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                inputText.setText((String) fontList.getSelectedValue());
            }
        });
        fontListBox.add(fontList);

        //Weight 创建并绑定对应盒子
        ButtonGroup weightGroup = new ButtonGroup();
        mediumButton = new JRadioButton("medium",true);
        mediumButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setWeight(Font.PLAIN);
            }
        });
        boldButton = new JRadioButton("bold");
        boldButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setWeight(Font.BOLD);
            }
        });
        weightGroup.add(mediumButton);
        weightGroup.add(boldButton);
        weightBox.add(mediumButton);
        weightBox.add(boldButton);

        //Slant 创建并绑定对应盒子
        ButtonGroup slantGroup = new ButtonGroup();
        romanButton = new JRadioButton("roman",true);
        romanButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setSlant(Font.PLAIN);
            }
        });
        italicButton = new JRadioButton("italic");
        italicButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setSlant(Font.ITALIC);
            }
        });
        slantGroup.add(romanButton);
        slantGroup.add(italicButton);
        slantBox.add(romanButton);
        slantBox.add(italicButton);

        //sizeSlider 创建并绑定对应盒子
        Box sizeBox = Box.createHorizontalBox();
        sizeSlider = new JSlider(10,20);
        sizeSlider.setMajorTickSpacing(1);
        sizeSlider.setMinorTickSpacing(1);
        sizeSlider.setPaintLabels(true);
        sizeSlider.setPaintTicks(true);
        sizeBox.add(sizeSlider);

        //Cancel And Ok 创建并绑定对应盒子
        cancelButton = new Button("Cancel");
        okButton = new Button("Ok");
        cancelButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setVisible(false);
            }
        });
        okButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setName(inputText.getText());
                setFontSize(sizeSlider.getValue());
                setVisible(false);
            }
        });
        buttonBox.add(cancelButton);
        buttonBox.add(okButton);

        //overall绑定与子盒子绑定
        add(overallBox);
        overallBox.add(new Label("The quick brown box.."));
        overallBox.add(familyBox);
        overallBox.add(fontListBox);
        overallBox.add(weightBox);
        overallBox.add(slantBox);
        overallBox.add(sizeBox);
        overallBox.add(buttonBox);

        //设置对话框的状态
        setBounds(100,100,300,300);
        setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);

    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    public int getWeight() {
        return weight;
    }

    public int getSlant() {
        return slant;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public void setSlant(int slant) {
        this.slant = slant;
    }

    public int getFontSize() {
        return fontSize;
    }

    public void setFontSize(int fontSize) {
        this.fontSize = fontSize;
    }
}

FontChooser类

package behaviorTypePattern.mediatorPattern.GUI;

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

public class FontChooser extends JFrame {
    JButton button;
    FontChooserDialog fontChooserDialog;
    JTextArea textArea;

    public FontChooser() throws HeadlessException {
        //采用流式布局
        setLayout(new FlowLayout());

        //创建文本区组件并放在最上边
        textArea = new JTextArea("点按钮改变文本风格");
        textArea.setColumns(20);
        textArea.setRows(2);
        add(textArea,BorderLayout.NORTH);

        //创建自定义对话框
        fontChooserDialog = new FontChooserDialog(this,"dialog");
        fontChooserDialog.setModal(true);

        //创建按钮并放在中间
        button = new JButton("font choose");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                fontChooserDialog.setVisible(true);//当点击按钮时弹出对话框
                Font font = getFontShape();//对话框结束时调用getFontShape()得到Font
                textArea.setFont(font);//并为文本区设置Font
            }
        });
        add(button,BorderLayout.CENTER);

        //设置Frame的状态
        setVisible(true);
        setBounds(100,100,300,300);
    }

    /**
     * 从FontChooserDialog得到Font的参数name,style,size,新建并返回一个Font
     * @return  font
     */
    public Font getFontShape() {
        String name = fontChooserDialog.getName();
        int style = fontChooserDialog.getWeight() + fontChooserDialog.getSlant();
        int size = fontChooserDialog.getFontSize();
        Font font = new Font(name,style,size);
        return font;
    }

    public static void main(String[] args) {
        FontChooser fontChooser = new FontChooser();
    }
}

运行代码

    public static void main(String[] args) {
        FontChooser fontChooser = new FontChooser();
    }

模式探讨

在我们大体实现了这个功能之后,我们来探讨背后的设计模式——中介者模式和观察者模式

虽然上面的代码看上去与Gof原书的代码实现不太相同,主要是语言【Java和C++】,另外Gof原书代码并没有写完整,但模式思想还是基于Gof。

中介者模式

我们自定义一个组件常用组合关系,一个父组件组合多个子组件。当子组件发生交互和通信时,这个时候我们可以**把父组件看作中介者。**如图,我们可以发现 FontChooser 组合了 FontChooserDialog 和 JTextArea 。当弹出的对话框被关闭时,FontChooser 还会调用 getFontStyle() 方法获得 Font,返回给 JTextArea,此时FontChooser就充当 FontChooserDialog 和 JTextArea的中介。

在这里插入图片描述

观察者模式

请仔细看看下面这段绑定监听事件的代码。

button = new JButton("font choose");
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        fontChooserDialog.setVisible(true);//当点击按钮时弹出对话框
        Font font = getFontShape();//对话框结束时调用getFontShape()得到Font
        textArea.setFont(font);//并为文本区设置Font
    }
});

这段代码背后隐藏着一个观察者模式【点击可了解】**。**我们可以从addActionListener()方法和ActionListener()接口看出来的。

在这里插入图片描述

经典的观察者模式的UML图如上图所示。其中抽象类 Subject 作为被观察者,接口 Observer作为观察者。Subject 和 Observer 为 多对一 的组合关系,从而达到对象之间相互组合的目的:当 Subject发生变化时,会调用notify()通知它注册的观察者,调用它的update()方法更新变化的数据。update()方法执行的是具体的响应逻辑。

当然,下面我们来看看 JButton 背后的点击事件原理。

请看基于JDK8源码分析的下图。

在这里插入图片描述

由UML图知道,

**ActionListener是一个接口,**需要实现抽象方法 actionPerformed。

**AbstractButton 是 JButton的父类。**其中有三个重要的方法

  • addActionListener(ActionListener l)添加 ActionListener

  • removeActionListener(ActionListener l)删除 ActionListener

  • fireActionPerformed(ActionEvent event)通知所有注册的 ActionListener。

ActionEvent 是动作事件。ActionEvent 作为一个对象由 AbstractButton 传递给 ActionListener, 也可以理解为事件源传递动作的事件给监听器对象。

ActionEvent包含事件发生的三个参数 when 时间、modifiers 用于确定鼠标或按键的标识符、 actionCommand 事件源的细节信息。帮助我们可以更方便排错。

总结

在我们理清了 JButton背后隐藏的观察者模式之后,我们可以自然地画出这张逻辑图。

在这里插入图片描述

参考:最后一张图灵感的来源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值