诗曰:

音讯传递需及时,一对多来通知忙。
观察者模式来相助,千里眼顺风耳现世。

📖 故事

师徒四人来到"消息传递岭",此地山高路远,音讯难通。

唐僧愁道:“悟空,为师要联系如来佛祖,告知取经进度,这可如何是好?”

悟空笑道:“师父莫急!徒儿这就施展观察者模式,让千里眼顺风耳现世!”

🐉 踩坑打怪:消息阻塞三难

第一难:轮询之苦

沙僧提议:“师父,要不咱们每隔一个时辰就去山顶看看有没有佛祖的消息?”

唐僧摇头:“太被动了!万一佛祖的消息来了,咱们在睡觉怎么办?”

八戒抱怨: “就是!老猪我刚睡着就被大师兄叫醒,说’去看看有没有消息’,结果啥也没有!”

// 轮询方式 - 累死人
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("到达女儿国");
    }
}

输出:

========== 取经开始 ==========
【唐僧】发送消息:离开大唐边境
【如来佛祖】收到消息:离开大唐边境
【观音菩萨】收到消息:离开大唐边境
【唐太宗】收到消息:离开大唐边境
【八戒】收到消息:离开大唐边境(关我啥事...)

========== 收服悟空 ==========
【唐僧】发送消息:收服孙悟空
【如来佛祖】收到消息:收服孙悟空
【观音菩萨】收到消息:收服孙悟空
【唐太宗】收到消息:收服孙悟空
【八戒】收到消息:收服孙悟空(关我啥事...)

🏆 战斗总结

唐僧赞叹: “悟空,这观察者模式果然精妙!为师只需发送一次消息,所有神仙都能收到!”

悟空得意: “师父,这还不止!您看——”

优点展示:

  1. 一对多通知:一次发送,多方接收
  2. 松耦合:唐僧不知道具体有哪些观察者
  3. 符合开闭原则:加新观察者不用改现有代码
  4. 支持广播通信:自动通知所有注册的观察者

八戒提问: “猴哥,要是某个观察者出错了,会不会影响其他人?”

悟空答: “好问题!应该在通知时加 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 架构
优点 松耦合、符合开闭原则、支持广播
缺点 循环依赖、性能问题(观察者太多)

下回预告

话说师徒继续西行,悟空觉得金箍棒威力不够,想要增强法力。如何在不修改金箍棒的前提下,给它添加新能力?

欲知后事如何,且听第五回分解!


Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐