设计模式中,Mediator (调停者)模式示例(完整代码 + 详细注释)一文详解!

Mediator 模式示例(完整代码 + 详细注释与布尔值解释)

下面是一个完整、可运行的 Java Swing 示例,演示如何使用中介者(Mediator)模式管理多个复选框与按钮之间的交互。代码之后包含详细注释和对 true / false 布尔值在每一步含义的逐行解释与示例场景,便于阅读和下载保存。


完整代码(可直接复制到 .java 文件编译运行)

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;

/**
 * 主程序入口
 */
public class Main {
    public static void main(String[] args) {
        // 创建订单窗口,并传入菜单项
        new OrderFrame("Hamburger", "Nugget", "Chip", "Coffee");
    }
}

/**
 * 订单窗口,包含多选框和操作按钮
 */
class OrderFrame extends JFrame {
    public OrderFrame(String... names) {
        setTitle("Order"); // 窗口标题
        setSize(460, 200); // 窗口大小
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口时退出程序
        Container c = getContentPane();
        c.setLayout(new FlowLayout(FlowLayout.LEADING, 20, 20));

        c.add(new JLabel("Use Mediator Pattern")); // 提示标签

        // 创建多选框列表
        List<JCheckBox> checkboxList = addCheckBox(names);

        // 创建按钮
        JButton selectAll = addButton("Select All");
        JButton selectNone = addButton("Select None");
        selectNone.setEnabled(false); // 初始不可点击
        JButton selectInverse = addButton("Inverse Select");

        // 创建中介对象,管理组件之间的交互
        new Mediator(checkboxList, selectAll, selectNone, selectInverse);

        setVisible(true); // 显示窗口
    }

    /**
     * 添加多选框
     */
    private List<JCheckBox> addCheckBox(String... names) {
        JPanel panel = new JPanel();
        panel.add(new JLabel("Menu:"));
        List<JCheckBox> list = new ArrayList<>();
        for (String name : names) {
            JCheckBox checkbox = new JCheckBox(name);
            list.add(checkbox);
            panel.add(checkbox);
        }
        getContentPane().add(panel);
        return list;
    }

    /**
     * 添加按钮
     */
    private JButton addButton(String label) {
        JButton button = new JButton(label);
        getContentPane().add(button);
        return button;
    }
}

/**
 * Mediator(中介者)类,负责管理多选框和按钮之间的交互逻辑
 */
class Mediator {
    private List<JCheckBox> checkBoxList;
    private JButton selectAll;
    private JButton selectNone;
    private JButton selectInverse;

    public Mediator(List<JCheckBox> checkBoxList, JButton selectAll, JButton selectNone, JButton selectInverse) {
        this.checkBoxList = checkBoxList;
        this.selectAll = selectAll;
        this.selectNone = selectNone;
        this.selectInverse = selectInverse;

        // 绑定多选框状态变化事件
        this.checkBoxList.forEach(checkBox -> checkBox.addChangeListener(this::onCheckBoxChanged));

        // 绑定按钮点击事件
        this.selectAll.addActionListener(this::onSelectAllClicked);
        this.selectNone.addActionListener(this::onSelectNoneClicked);
        this.selectInverse.addActionListener(this::onSelectInverseClicked);
    }

    /**
     * 当多选框状态发生变化时,更新按钮状态
     */
    public void onCheckBoxChanged(ChangeEvent event) {
        boolean allChecked = true;
        boolean allUnchecked = true;

        for (var checkBox : checkBoxList) {
            if (checkBox.isSelected()) {
                allUnchecked = false; // 只要有选中,说明不是全部未选
            } else {
                allChecked = false; // 只要有未选,说明不是全部选中
            }
        }

        selectAll.setEnabled(!allChecked); // 全选按钮:如果已经全部选中,则禁用
        selectNone.setEnabled(!allUnchecked); // 取消按钮:如果全部未选中,则禁用
    }

    /**
     * 点击“全选”按钮时
     */
    public void onSelectAllClicked(ActionEvent event) {
        checkBoxList.forEach(checkBox -> checkBox.setSelected(true));
        selectAll.setEnabled(false);
        selectNone.setEnabled(true);
    }

    /**
     * 点击“取消所有”按钮时
     */
    public void onSelectNoneClicked(ActionEvent event) {
        checkBoxList.forEach(checkBox -> checkBox.setSelected(false));
        selectAll.setEnabled(true);
        selectNone.setEnabled(false);
    }

    /**
     * 点击“反选”按钮时
     */
    public void onSelectInverseClicked(ActionEvent event) {
        checkBoxList.forEach(checkBox -> checkBox.setSelected(!checkBox.isSelected()));
        // 更新按钮状态
        onCheckBoxChanged(null);
    }
}

🔹 代码解析(简要)

OrderFrame 类

  • 创建窗口、添加多选框和按钮。
  • 将组件交给 Mediator 来管理交互逻辑。
  • 通过 addCheckBoxaddButton 方法简化组件创建。

Mediator 类

  • 核心思想:中介者负责所有组件之间的交互,组件之间不直接引用彼此,降低耦合。
  • 当多选框变化时,会调用 onCheckBoxChanged 更新按钮状态。
  • 当按钮点击时,会根据按钮类型修改多选框的状态,并更新其他按钮状态。

交互逻辑

  • Select All:全选多选框,并禁用自己。
  • Select None:取消所有多选框,并禁用自己。
  • Inverse Select:反选多选框,同时更新按钮状态。
  • 多选框改变时,会动态启用或禁用按钮,使 UI 始终与当前状态保持一致。

中介模式优势

  • 多个组件的交互逻辑集中在 Mediator 中管理,避免了复杂的双向依赖。
  • 便于扩展:如果增加新按钮,只需在 Mediator 中添加逻辑即可。

✅ 布尔值(true / false)逐步详解 — 逐条解释(便于理解每一步)

下面把所有与 true / false 有关的语义逐行解释清楚,并配上具体例子(4 个 checkbox 场景)和真值表,帮助你把逻辑“看得见、追得清”。

先约定几个基本概念(短小、必须懂)

  • checkBox.isSelected():返回 true 表示该复选框“被选中(有勾)”,false 表示未选中(没勾)。
  • checkBox.setSelected(true/false):把复选框设为选中或未选中(程序或用户都可以触发)。
  • button.setEnabled(true/false)true → 按钮可点击(有作用);false → 按钮不可点击/灰化(没有作用)。
  • !:逻辑非运算符,!truefalse!falsetrue

onCheckBoxChanged 中两变量的含义(关键)

boolean allChecked = true;
boolean allUnchecked = true;
for (var checkBox : checkBoxList) {
    if (checkBox.isSelected()) {
        allUnchecked = false;
    } else {
        allChecked = false;
    }
}
selectAll.setEnabled(!allChecked);
selectNone.setEnabled(!allUnchecked);

逐句解释:

  1. boolean allChecked = true;
    一开始假设“所有 checkbox 都是被选中的”。把初始值设为 true 是为了用“找反例”的方式来判断“是不是全部选中”:只要在循环中发现 任意一个 未选 (isSelected() == false),就把 allChecked 设为 false。这是常见的“全为真”判定模式(conjunction)。

  2. boolean allUnchecked = true;
    同理,初始假设“所有 checkbox 都是未选中的”。只要发现任意一个被选中,就把 allUnchecked 设为 false

  3. if (checkBox.isSelected()) { allUnchecked = false; } else { allChecked = false; }

    • 如果当前这个 checkbox 被选中:说明“不是所有都未选中”,所以 allUnchecked = false
    • 否则(当前未选中):说明“不是所有都被选中”,所以 allChecked = false
      经过对每个 checkbox 做判断后:
    • allChecked == true 只有当“遍历过程中没有发现任何未选项” —— 即每个 checkbox 都是选中
    • allUnchecked == true 只有当“遍历过程中没有发现任何被选项” —— 即每个 checkbox 都是未选中
      如果既不是全部选中也不是全部未选中(即“混合”状态),两个变量都会是 false
  4. selectAll.setEnabled(!allChecked);

    • 如果 allChecked == true(已经全部选中),!allCheckedfalseselectAll.setEnabled(false) → 禁用“Select All”按钮(因为按它没有意义)。
    • 如果 allChecked == false(没有全部选中),!allCheckedtrue → 启用“Select All”。
      换句话说:只有在“非全部选中”时,才允许用户点击“全选”。
  5. selectNone.setEnabled(!allUnchecked);

    • 如果 allUnchecked == true(全部未选中),!allUncheckedfalse → 禁用“Select None”。
    • 如果 allUnchecked == false(有至少一个被选中),!allUncheckedtrue → 启用“Select None”。
      换句话说:只有在“存在已选项”时,才允许用户点击“取消所有”。

真值表(以 4 个复选框为例)

复选框状态描述 allChecked allUnchecked selectAll(enabled) selectNone(enabled)
全部未选(0个被选) true true !true → false !true → false
部分被选(混合) false false !false → true !false → true
全部被选(4个被选) true false !true → false !false → true

简言之:

  • 全部未选:两个按钮都禁用(没有意义去“全选”或“全不选”)。
  • 部分选中(混合):两个按钮都启用(用户可以选择“全选”或“取消所有”)。
  • 全部选中:Select All 禁用Select None 启用

举几个具体步骤演示(手把手)

场景:4 个 checkbox: A B C D

场景 1 — 初始:全部未选

  • 遍历:每个 .isSelected() 都是 false,因此 allChecked 在第一次遇到未选时被设为 false,但 allUnchecked 保持 true
  • 结果:allChecked = falseallUnchecked = trueselectAll.setEnabled(!false)true(可点);selectNone.setEnabled(!true)false(不可点)。

场景 2 — 用户点 A(只有 A 被选)

  • 遍历时:遇到 A 是 trueallUnchecked = false;遇到 B/C/D 为 falseallChecked = false
  • 结果:allChecked = false, allUnchecked = false → 两按钮都 setEnabled(true)(都可点)。

场景 3 — 用户点“Select All”按钮(把所有设成 true

  • 程序执行 checkBox.setSelected(true) 把每项设为选中。最终 allChecked = true, allUnchecked = false
  • selectAll.setEnabled(!allChecked)false(禁用全选),selectNone.setEnabled(!allUnchecked)true(启用取消)。

场景 4 — 用户点“Inverse Select”(反选每一项)

  • 每一项变为 !isSelected()。程序最后调用 onCheckBoxChanged(null) 来重新计算 allChecked / allUnchecked 并根据上面规则设置按钮状态。
    • 例如从全部未选(0000)反选 → 全部选(1111),就变成场景 3 的结果。
    • 从部分选(例如 1000)反选 → 0111(混合)→ 两按钮都可用。

常见疑问 & 额外说明

Q:为什么初始用 true 而不是 false

A:这是判断“全部某个条件成立”的常用技巧:

  • 假设“全部为真(true)”,只要发现一个反例就把它设为 false
  • 如果初始用 false,你就必须把每次都 && 或者计数,逻辑更绕。用“先 assume true,再找反例”很直观也高效。

Q:setSelected(...) 会否触发监听器(顺序问题)?

A:在 Swing 中,调用 setSelected(...) 会改变模型,通常会触发相应的 ChangeListener/ItemListener。因此在 onSelectAllClicked 里把所有 setSelected(true) 做完后,onCheckBoxChanged 很可能被触发多次(每次单个 checkbox 改变都会触发)。

为了稳妥和明确,代码里同时显式设置按钮状态(selectAll.setEnabled(false); selectNone.setEnabled(true);),这样不会依赖监听器的调用时序来保证最终状态一致。

onSelectInverseClicked 中选择显式调用 onCheckBoxChanged(null) 是直接重用该方法来“统一计算最终按钮状态”,也可以保证最终状态正确。

Q:有没有其他实现方式?

A:有几种替代或优化方案:

  • 使用计数器 int selectedCount:遍历统计 selectedCount,然后判断 selectedCount == total(全部选中),selectedCount == 0(全部未选中),这在逻辑上更直观一些。
  • 如果担心监听器被多次触发,可在修改前临时移除监听器、修改、再加回,或在修改时用一个“批量更新”标志位(updating = true)在监听器里短路(监听器开头 if (updating) return;),最后再统一更新 UI。

总结

  • allCheckedallUnchecked 用来分别表示“是否全部选中”和“是否全部未选”。
  • selectAll.setEnabled(!allChecked) 的意思是:当且仅当“不是全部选中”时,启用“全选”按钮;否则禁用。
  • selectNone.setEnabled(!allUnchecked) 类似:当且仅当存在已选项时,启用“取消所有”按钮
  • ! 是布尔取反 —— 它把“是否全部选中”转换为“是否应该启用全选按钮”的布尔值。

Java 方法引用(::)详解

1. 基本含义

在 Java 里,:: 叫做 方法引用(Method Reference)
它是 Java 8 引入的一种简写语法,用来代替 lambda 表达式。

例子

this::onCheckBoxChanged

等价于:

(event) -> this.onCheckBoxChanged(event)

说明:

  • 左边(this):表示调用者(这里是当前 Mediator 对象)。
  • 右边(onCheckBoxChanged):表示要调用的方法名。

所以 this::onCheckBoxChanged 就是 “把 Mediator 的 onCheckBoxChanged 方法当作函数对象传递出去”


2. 在代码中的具体应用

checkBox.addChangeListener(this::onCheckBoxChanged);

等价于:

checkBox.addChangeListener(event -> this.onCheckBoxChanged(event));

addChangeListener 需要一个 ChangeListener,它的函数式接口定义是:

public interface ChangeListener extends EventListener {
    void stateChanged(ChangeEvent e);
}
  • 需要传入一个方法:形参是 ChangeEvent,返回值是 void
  • onCheckBoxChanged(ChangeEvent event) 方法正好符合要求。

因此,Java 允许你直接写 this::onCheckBoxChanged,编译器会自动匹配成对应的函数接口实现。


3. 方法引用的几种形式

为了更全面理解,你可以记住 ::四种主要用法

1)对象::实例方法

this::onCheckBoxChanged
// 等价于 (e) -> this.onCheckBoxChanged(e)

2)类::静态方法

Math::abs
// 等价于 x -> Math.abs(x)

3)类::实例方法(特殊)

String::toLowerCase
// 等价于 s -> s.toLowerCase()

4)类::构造方法

ArrayList::new
// 等价于 () -> new ArrayList<>()

4. 总结

  • ::方法引用,是 lambda 表达式的简写
  • this::onCheckBoxChanged(event) -> this.onCheckBoxChanged(event)
  • 好处:代码更简洁、可读性更高
posted @ 2025-09-08 20:39  AlphaGeek  阅读(8)  评论(0)    收藏  举报