You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:45:16