Java 设计模式西游篇 - 第四回:观察者模式传音讯 千里眼顺风耳现世
·
诗曰:
音讯传递需及时,一对多来通知忙。
观察者模式来相助,千里眼顺风耳现世。
📖 故事
师徒四人来到"消息传递岭",此地山高路远,音讯难通。
唐僧愁道:“悟空,为师要联系如来佛祖,告知取经进度,这可如何是好?”
悟空笑道:“师父莫急!徒儿这就施展观察者模式,让千里眼顺风耳现世!”
🐉 踩坑打怪:消息阻塞三难
第一难:轮询之苦
沙僧提议:“师父,要不咱们每隔一个时辰就去山顶看看有没有佛祖的消息?”
唐僧摇头:“太被动了!万一佛祖的消息来了,咱们在睡觉怎么办?”
八戒抱怨: “就是!老猪我刚睡着就被大师兄叫醒,说’去看看有没有消息’,结果啥也没有!”
// 轮询方式 - 累死人
public class TangSeng {
public void waitForMessage() {
while (true) {
// 每隔一段时间检查一次
checkMessage();
try {
Thread.sleep(3600000); // 一小时检查一次
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
悟空吐槽: “这太蠢了!明明可以等消息来了主动通知咱们!”
第二难:紧耦合之困
悟空想了个办法:
public class TangSeng {
private Rulai佛祖 rulai = new Rulai佛祖();
public void checkMessage() {
rulai.getMessage();
}
}
八戒发现 bug: “猴哥!要是佛祖换成观音菩萨通知咱们呢?你这代码得改啊!”
悟空皱眉:“正是!这耦合太紧了!”
第三难:一对多之乱
唐僧又道:“悟空,不止佛祖,为师还要让大唐皇帝、观音菩萨、各路神仙都知道咱们的进度。”
悟空傻眼:“师父,那俺得给每个人都写个通知方法?”
public class Wukong {
public void notifyProgress() {
rulai佛祖.notify();
guanyin菩萨.notify();
tangEmperor.notify();
// 每多一个观察者,就要加一行...
}
}
悟空怒道: “这代码太丑了!每多一个观察者,俺就要改代码!”
⚔️ 观察者模式大法
第一步:定义观察者接口
// 观察者接口
public interface Observer {
void update(String message);
}
第二步:实现各种观察者
// 如来佛祖观察者
public class RulaiObserver implements Observer {
@Override
public void update(String message) {
System.out.println("【如来佛祖】收到消息:" + message);
}
}
// 观音菩萨观察者
public class GuanyinObserver implements Observer {
@Override
public void update(String message) {
System.out.println("【观音菩萨】收到消息:" + message);
}
}
// 唐太宗观察者
public class EmperorObserver implements Observer {
@Override
public void update(String message) {
System.out.println("【唐太宗】收到消息:" + message);
}
}
// 八戒观察者(凑热闹的)
public class BajieObserver implements Observer {
@Override
public void update(String message) {
System.out.println("【八戒】收到消息:" + message + "(关我啥事...)");
}
}
第三步:定义被观察者(主题)
// 被观察者接口
public interface Subject {
void attach(Observer observer); // 添加观察者
void detach(Observer observer); // 移除观察者
void notifyObservers(String message); // 通知所有观察者
}
// 唐僧是被观察者
public class TangSeng implements Subject {
private List<Observer> observers = new ArrayList<>();
@Override
public void attach(Observer observer) {
observers.add(observer);
System.out.println("【系统】添加观察者:" + observer.getClass().getSimpleName());
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
System.out.println("【系统】移除观察者:" + observer.getClass().getSimpleName());
}
@Override
public void notifyObservers(String message) {
System.out.println("【唐僧】发送消息:" + message);
for (Observer observer : observers) {
observer.update(message);
}
}
// 业务方法
public void makeProgress(String progress) {
System.out.println("【唐僧】取经进度:" + progress);
notifyObservers(progress); // 通知所有观察者
}
}
第四步:实战演练
public class Main {
public static void main(String[] args) {
// 创建唐僧(被观察者)
TangSeng tangSeng = new TangSeng();
// 创建观察者
Observer rulai = new RulaiObserver();
Observer guanyin = new GuanyinObserver();
Observer emperor = new EmperorObserver();
Observer bajie = new BajieObserver();
// 注册观察者
tangSeng.attach(rulai);
tangSeng.attach(guanyin);
tangSeng.attach(emperor);
tangSeng.attach(bajie);
System.out.println("========== 取经开始 ==========");
tangSeng.makeProgress("离开大唐边境");
System.out.println("\n========== 收服悟空 ==========");
tangSeng.makeProgress("收服孙悟空");
System.out.println("\n========== 收服八戒 ==========");
tangSeng.makeProgress("收服猪八戒");
// 八戒太吵了,移除他
tangSeng.detach(bajie);
System.out.println("\n========== 到达女儿国 ==========");
tangSeng.makeProgress("到达女儿国");
}
}
输出:
========== 取经开始 ==========
【唐僧】发送消息:离开大唐边境
【如来佛祖】收到消息:离开大唐边境
【观音菩萨】收到消息:离开大唐边境
【唐太宗】收到消息:离开大唐边境
【八戒】收到消息:离开大唐边境(关我啥事...)
========== 收服悟空 ==========
【唐僧】发送消息:收服孙悟空
【如来佛祖】收到消息:收服孙悟空
【观音菩萨】收到消息:收服孙悟空
【唐太宗】收到消息:收服孙悟空
【八戒】收到消息:收服孙悟空(关我啥事...)
🏆 战斗总结
唐僧赞叹: “悟空,这观察者模式果然精妙!为师只需发送一次消息,所有神仙都能收到!”
悟空得意: “师父,这还不止!您看——”
优点展示:
- 一对多通知:一次发送,多方接收
- 松耦合:唐僧不知道具体有哪些观察者
- 符合开闭原则:加新观察者不用改现有代码
- 支持广播通信:自动通知所有注册的观察者
八戒提问: “猴哥,要是某个观察者出错了,会不会影响其他人?”
悟空答: “好问题!应该在通知时加 try-catch,一个观察者出错不影响其他人!”
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
try {
observer.update(message);
} catch (Exception e) {
System.err.println("观察者 " + observer + " 出错了:" + e.getMessage());
}
}
}
Java 内置的观察者
沙僧发现: “大师兄,Java 好像自带观察者?”
悟空点头: “正是!Java 有内置的 Observer 接口和 Observable 类!”
import java.util.Observable;
import java.util.Observer;
// 被观察者
public class TangSeng extends Observable {
public void makeProgress(String progress) {
setChanged(); // 标记已改变
notifyObservers(progress); // 通知所有观察者
}
}
// 观察者
public class RulaiObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("【如来佛祖】收到消息:" + arg);
}
}
悟空补充: “不过 Java 9 之后不推荐用这个了,还是自己实现或者用第三方库更好!”
本章要点
| 要点 | 说明 |
|---|---|
| 核心思想 | 定义对象间的一对多依赖,一个对象改变,所有依赖者自动收到通知 |
| 适用场景 | 事件处理、消息推送、MVC 架构 |
| 优点 | 松耦合、符合开闭原则、支持广播 |
| 缺点 | 循环依赖、性能问题(观察者太多) |
下回预告
话说师徒继续西行,悟空觉得金箍棒威力不够,想要增强法力。如何在不修改金箍棒的前提下,给它添加新能力?
欲知后事如何,且听第五回分解!
更多推荐




所有评论(0)