设计模式中,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来管理交互逻辑。 - 通过
addCheckBox和addButton方法简化组件创建。
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→ 按钮不可点击/灰化(没有作用)。!:逻辑非运算符,!true→false,!false→true。
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);
逐句解释:
-
boolean allChecked = true;
一开始假设“所有 checkbox 都是被选中的”。把初始值设为true是为了用“找反例”的方式来判断“是不是全部选中”:只要在循环中发现 任意一个 未选 (isSelected() == false),就把allChecked设为false。这是常见的“全为真”判定模式(conjunction)。 -
boolean allUnchecked = true;
同理,初始假设“所有 checkbox 都是未选中的”。只要发现任意一个被选中,就把allUnchecked设为false。 -
if (checkBox.isSelected()) { allUnchecked = false; } else { allChecked = false; }- 如果当前这个 checkbox 被选中:说明“不是所有都未选中”,所以
allUnchecked = false。 - 否则(当前未选中):说明“不是所有都被选中”,所以
allChecked = false。
经过对每个 checkbox 做判断后: allChecked == true只有当“遍历过程中没有发现任何未选项” —— 即每个 checkbox 都是选中。allUnchecked == true只有当“遍历过程中没有发现任何被选项” —— 即每个 checkbox 都是未选中。
如果既不是全部选中也不是全部未选中(即“混合”状态),两个变量都会是false。
- 如果当前这个 checkbox 被选中:说明“不是所有都未选中”,所以
-
selectAll.setEnabled(!allChecked);- 如果
allChecked == true(已经全部选中),!allChecked为false→selectAll.setEnabled(false)→ 禁用“Select All”按钮(因为按它没有意义)。 - 如果
allChecked == false(没有全部选中),!allChecked为true→ 启用“Select All”。
换句话说:只有在“非全部选中”时,才允许用户点击“全选”。
- 如果
-
selectNone.setEnabled(!allUnchecked);- 如果
allUnchecked == true(全部未选中),!allUnchecked为false→ 禁用“Select None”。 - 如果
allUnchecked == false(有至少一个被选中),!allUnchecked为true→ 启用“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 = false,allUnchecked = true→selectAll.setEnabled(!false)→true(可点);selectNone.setEnabled(!true)→false(不可点)。
场景 2 — 用户点 A(只有 A 被选)
- 遍历时:遇到 A 是
true→allUnchecked = false;遇到 B/C/D 为false→allChecked = 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。
总结
allChecked、allUnchecked用来分别表示“是否全部选中”和“是否全部未选”。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)。- 好处:代码更简洁、可读性更高。

浙公网安备 33010602011771号