Observer模式Push通信风格灵活性疑问:修改传递对象的含义
Observer模式Push风格第二种方案详解
针对你学习Observer模式时遇到的几个疑问,下面逐个拆解说明:
1. “修改传递的对象”具体指什么?
说白了,这种方案就是创建一个专门的「状态容器类」,把Subject中需要通知给Observer的所有状态都封装到这个类里,代替原来直接传递零散参数(比如你代码里的value和value2)的方式。
给你改个例子,对应你的ConcreteSubject,可以写一个这样的容器类:
// 专门用来装Subject状态的容器类 public class ConcreteSubjectState { private Integer value; // 用包装类支持null private String value2; // 构造器+getter,不用setter因为状态是一次性传递的 public ConcreteSubjectState(Integer value, String value2) { this.value = value; this.value2 = value2; } public Integer getValue() { return value; } public String getValue2() { return value2; } }
然后把Observer接口和Subject的通知逻辑改成:
// Observer接口不再需要泛型,统一接收容器对象 public interface Observer { void update(ConcreteSubjectState state); } // Subject的通知方法调整 public class Subject { private List<Observer> observers = new ArrayList<>(); // 增删Observer的方法不变 public void addObserver(Observer observer) { observers.add(observer); } public void removeObserver(Observer observer) { observers.remove(observer); } public void notifyObservers(ConcreteSubjectState state) { for (Observer observer : observers) observer.update(state); } }
ConcreteSubject的通知逻辑也跟着改:
public class ConcreteSubject extends Subject { private int value; private String value2; public void setValue(int value) { this.value = value; // 把状态打包成容器对象传递 notifyObservers(new ConcreteSubjectState(value, null)); } public void setValue2(String value2) { this.value2 = value2; notifyObservers(new ConcreteSubjectState(null, value2)); } }
2. 用泛型能传任意内容,为什么还要用这种方案?
你的泛型方案确实灵活,但存在几个硬伤:
- 可读性拉胯:如果以后要加第三个、第四个参数,泛型会变成
<T,U,V,W>,方法签名越来越臃肿,维护起来头大; - 类型安全没保障:泛型擦除后,运行时没法保证传递的参数类型符合Observer的预期,很容易踩
ClassCastException的坑; - 冗余代码多:每个Observer都要写一堆
if (xxx != null)来筛选自己需要的参数,重复又容易漏; - 违反开闭原则:如果要新增参数,还是得修改
Observer接口的update方法签名(比如从<T,U>改成<T,U,V>),所有实现类都要跟着改——这正是Push模式原本要解决的灵活性问题,等于绕回去了。
而容器类的方案,新增字段只需要在容器类里加,Observer接口和update方法完全不用动,真正做到了对扩展开放、对修改关闭。
3. “在该类新增字段”中的“该类”指哪个类?
这里的“该类”就是上面说的状态容器类(比如ConcreteSubjectState),不是Subject也不是Observer实现类。所有需要通知的新状态,都往这个容器类里加字段就行。
4. 这么做如何让ConcreteObserver2仅接收特定字段的通知?
首先,容器类只是把所有状态打包传递,Observer可以在update方法里只处理自己关心的字段:
public class ConcreteObserver2 implements Observer { @Override public void update(ConcreteSubjectState state) { String value2 = state.getValue2(); if (value2 != null) { System.out.println("ConcreteObserver2 got notified. value2 is: " + value2); } } }
如果要实现仅在value2变化时才通知ConcreteObserver2(而不是每次状态变化都发通知),可以给容器类加个事件类型标记:
public class ConcreteSubjectState { // 标记是哪种状态变化 public enum EventType { VALUE_UPDATED, VALUE2_UPDATED } private EventType eventType; private Integer value; private String value2; // 构造器带上事件类型 public ConcreteSubjectState(EventType eventType, Integer value, String value2) { this.eventType = eventType; this.value = value; this.value2 = value2; } // getter方法 public EventType getEventType() { return eventType; } public Integer getValue() { return value; } public String getValue2() { return value2; } }
然后ConcreteSubject通知时指定类型:
public void setValue2(String value2) { this.value2 = value2; notifyObservers(new ConcreteSubjectState(ConcreteSubjectState.EventType.VALUE2_UPDATED, null, value2)); }
ConcreteObserver2就可以只处理指定类型的事件:
@Override public void update(ConcreteSubjectState state) { if (ConcreteSubjectState.EventType.VALUE2_UPDATED.equals(state.getEventType())) { System.out.println("ConcreteObserver2 got notified. value2 is: " + state.getValue2()); } }
这样就精准实现了只接收特定字段的通知。
内容的提问来源于stack exchange,提问作者Mehdi
相关产品推荐
相关产品推荐

