观察者模式(Observer)
意图
定义对象间的一对多依赖关系,当一个对象改变状态时,所有依赖者都会收到通知并自动更新。
UML 图
优点
- 松耦合:主题和观察者之间抽象耦合,彼此不需要知道具体实现
- 支持广播通信:一个主题可以通知多个观察者
- 开闭原则:可以轻松添加新的观察者而不修改主题
- 状态同步:确保所有观察者及时获得状态变化
- 职责分离:将观察者与主题的逻辑分离,提高可维护性
缺点
- 通知顺序不可控:观察者收到通知的顺序可能不确定
- 性能问题:如果观察者数量很多,通知过程可能耗时
- 循环依赖:不正确的实现可能导致循环调用
- 内存泄漏:如果观察者没有正确注销,可能导致内存泄漏
- 过度更新:细小的状态变化可能触发不必要的更新
代码示例
以机器人和人类为例,机器人做饭后通知人类用餐:
1. 观察者接口 (Observer Interface)
// 观察者接口 - 用餐者
public interface MealObserver {void update(String mealName);String getName();
}
2. 主题接口 (Subject Interface)
// 主题接口 - 厨师
public interface CookSubject {void attach(MealObserver observer);void detach(MealObserver observer);void notifyObservers();
}
3. 具体主题 (Concrete Subject)
// 机器人厨师 - 具体主题
public class RobotCook implements CookSubject {private String currentMeal;private List<MealObserver> observers = new ArrayList<>();@Overridepublic void attach(MealObserver observer) {observers.add(observer);System.out.println(observer.getName() + " 开始等待用餐");}@Overridepublic void detach(MealObserver observer) {observers.remove(observer);System.out.println(observer.getName() + " 取消用餐等待");}@Overridepublic void notifyObservers() {System.out.println("🍳 机器人厨师通知: " + currentMeal + " 已准备好!");for (MealObserver observer : observers) {observer.update(currentMeal);}}// 做饭方法public void cookMeal(String mealName) {System.out.println("🤖 机器人开始烹饪: " + mealName);this.currentMeal = mealName;// 模拟烹饪过程try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}mealReady();}private void mealReady() {System.out.println("✅ " + currentMeal + " 烹饪完成!");notifyObservers();}public String getCurrentMeal() {return currentMeal;}
}
4. 具体观察者 (Concrete Observers)
// 人类 - 具体观察者
public class Human implements MealObserver {private String name;public Human(String name) {this.name = name;}@Overridepublic void update(String mealName) {System.out.println("📢 " + name + " 收到通知: " + mealName + " 已准备好!");eat(mealName);}public void eat(String mealName) {System.out.println("🍽️ " + name + " 正在享用: " + mealName);}@Overridepublic String getName() {return name;}
}// 其他类型观察者 - 比如另一个机器人也可以观察
public class OtherRobot implements MealObserver {private String name;public OtherRobot(String name) {this.name = name;}@Overridepublic void update(String mealName) {System.out.println("🔋 " + name + " 收到通知: 开始充电模式");// 机器人可能不需要吃饭,但可以执行其他操作}@Overridepublic String getName() {return name;}
}
5. 客户端代码 (Client Code)
public class ObserverPatternDemo {public static void main(String[] args) {// 创建机器人厨师RobotCook robotCook = new RobotCook();// 创建人类观察者Human john = new Human("John");Human alice = new Human("Alice");Human bob = new Human("Bob");// 创建其他类型观察者OtherRobot helperBot = new OtherRobot("HelperBot");// 注册观察者robotCook.attach(john);robotCook.attach(alice);robotCook.attach(helperBot);System.out.println("\n=== 第一次烹饪 ===");// 机器人开始做饭robotCook.cookMeal("红烧牛肉面");System.out.println("\n=== 调整观察者 ===");// Bob加入等待,Alice取消等待robotCook.attach(bob);robotCook.detach(alice);System.out.println("\n=== 第二次烹饪 ===");// 机器人做另一顿饭robotCook.cookMeal("蔬菜沙拉");}
}
在Java标准库中的应用
观察者模式在Java标准库中有广泛应用:
- JavaBeans属性变更监听
// 属性变更支持
public class BeanWithProperty {private String value;private PropertyChangeSupport support = new PropertyChangeSupport(this);public void setValue(String newValue) {String oldValue = this.value;this.value = newValue;support.firePropertyChange("value", oldValue, newValue);}public void addPropertyChangeListener(PropertyChangeListener listener) {support.addPropertyChangeListener(listener);}
}
- Swing事件监听
JButton button = new JButton("Click me");
// 添加观察者(事件监听器)
button.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("Button clicked!");}
});
- JavaFX属性绑定
SimpleStringProperty name = new SimpleStringProperty("John");
// 观察属性变化
name.addListener((observable, oldValue, newValue) -> {System.out.println("Name changed from " + oldValue + " to " + newValue);
});
- Servlet上下文监听
@WebListener
public class AppContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {// 应用启动时通知}@Overridepublic void contextDestroyed(ServletContextEvent sce) {// 应用关闭时通知}
}
总结
观察者模式通过定义一对多的依赖关系,实现了对象间的松耦合通信。在机器人做饭通知人类的例子中,机器人作为主题负责状态改变和通知,人类作为观察者接收通知并作出响应。这种模式特别适合需要实现事件通知、状态同步和动态响应的场景,如GUI事件处理、消息推送系统、实时数据更新等。Java标准库中的事件监听机制和属性变更通知都是观察者模式的典型应用。