Observer(观察者) --对象行为型模式

1.意图

定义对象间的一种依赖关系。当一个对象的状态发生改变时,所有依赖与它的对象得到通知并自动更新。

2.别名

发布-订阅(publish-subscribe)

3.动机

现在有一个数据对象,还有表格对象和柱状图对象来描述这个数据对象的信息,因此数据对象的任何改动都应该立刻通知它们。

Observer模式描述了如何建立这种关系。

4.适用性

·一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中,以使它们可以各自独立地改变和复用。

·对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。

你不希望这些对象是紧密耦合的。

5.结构

6.参与者

·Subject(目标)

—目标知道它的观察者。可以有任意多个观察者观察同一个目标。

—提供注册和删除观察者对象的接口。

·Observer(观察者)

—为那些在目标发生改变时需要获得通知的对象定义一个更新接口。

·ConcreteSubject(具体目标)

—将有关状态存入各ConcreteObserver对象。

—当它的状态发生改变时,向其各个观察者发出通知。

·ConcreteObserver(具体观察者)

—维护一个指向ConcreteSubject对象的引用。

—存储有关状态,这些状态应与目标的状态保持一致。

—实现Observer的更新接口,以使自身状态与目标的状态保持一致。

7.协作

8.效果

1)抽象耦合而非紧密耦合

对于subject来说,它只知道它有很多observer,有一些简单接口,并不知道它们属于哪个具体类。

2)广播通信

subject唯一的责任就是通知它的observer,处理还是忽略取决于observer

3)更新的后果

由于一个observer并不知道其他observer的存在,它可能对改变subject的最终代价一无所知。

9.实现

1)创建从subject到observer的映射

hash,不赖

2)观察多个subject

一个observer可能观察多个subject,例如一张表格依赖于多个数据源。此时就需要拓展Update来传递关于subject的信息,可以直接将subject作为参数传递。

3)谁来调用Notify来触发更新

两个选择:

改变subject后自动调用。用户得了MVP,自动程序是躺赢狗。

用户来调用,这样就可以在一系列状态改变后一次性更新(commit),但是用户可能有点健忘。

4)悬挂引用(“空指针问题”)

删除一个目标时应注意不要在其观察者中遗留对该目标的悬挂引用。一种避免悬挂引用的方法是,当一个目标被删除时,让它通知它的观察者将对该目标的引用复位。不能简单地删除观察者,因为可能它们还在观察其他的目标。

5)保证state一致

嵌套调用:子类重写父类方法时意外触发父类逻辑

public abstract class Subject {
    // 父类定义的状态变更方法
    public void updateState() {
        // 基础状态更新逻辑
        doUpdate();
        notifyObservers(); // 触发通知
    }

    protected abstract void doUpdate(); // 子类扩展点
}

public class ConcreteSubject extends Subject {
    private int value;

    @Override
    protected void doUpdate() {
        value++; // 子类扩展的状态修改
        // 假设此处误调用了父类的updateState(),导致重复通知
        super.updateState(); // 错误:触发父类通知逻辑
    }
}
  1. 父类 updateState() 调用 doUpdate()

  2. 子类 doUpdate() 调用 super.updateState()

  3. 父类 updateState() 再次触发 notifyObservers()
    最终导致 notifyObservers() 被递归调用多次。

首次 notifyObservers() 时,value 可能仅部分更新,观察者获取到中间状态。完辣!

你可以用抽象的Subject类中的模板方法(Template Method)发送通知来避免这种错误。定义那些子类可以重定义的原语操作,并将Notify作为模板方法中的最后一个操作,这样当子类重定义了Subject的操作时,还可以保证该对象的状态是自身一致的。

public abstract class Subject {
    // 模板方法:定义算法骨架(不可重写)
    public final void performUpdate() {
        beforeUpdate();   // 前置钩子(可选)
        doUpdate();       // 子类实现具体状态变更
        afterUpdate();    // 后置钩子(可选)
        notifyObservers(); // 确保在状态一致后触发通知
    }

    // 原语操作:子类必须实现的纯状态变更逻辑
    protected abstract void doUpdate();

    // 可选的扩展点(空实现或默认逻辑)
    protected void beforeUpdate() {}
    protected void afterUpdate() {}
}
public class ConcreteSubject extends Subject {
    private int value;
    private String status;

    @Override
    protected void doUpdate() {
        value++;
        status = (value % 2 == 0) ? "EVEN" : "ODD"; // 原子性更新
    }
}
Subject subject = new ConcreteSubject();
subject.performUpdate(); // 状态变更和通知由模板方法严格管控

6)推/拉模型

subject通常需要广播一些其他的信息,这些信息作为Update的一个参数传递出去。

推模型:发送关于改变的详细信息,不管observer是否需要。

拉模型:发送最小的通知,由observer询问细节。

拉模型强调的是目标不知道它的observer,而推模型假定目标知道一些observer需要的信息。

推模型可能使得observer相对难以复用,因为subject对observer的假定可能并不总是正确的。拉模型可能效率较差。

7)指定具体的改变类型

你可以拓展Add接口,让各个observer注册为仅仅对特定事件“感兴趣”,这么做提高了更新的效率。一种实现方式是使用Aspect:

  1. 事件类型(Aspect):观察者可注册为对特定事件类型(如 "DATA_UPDATE""STATUS_CHANGE")感兴趣。

  2. 高效通知:事件触发时,仅通知订阅该事件类型的观察者。

  3. 解耦参数传递:将事件类型和数据封装为事件对象,传递上下文信息。

// 事件对象:封装事件类型和相关数据
public class Event {
    private final String type;
    private final Object data;
}

// 观察者接口:支持按事件类型处理
public interface Observer {
    void update(Event event);
}
public class Subject {
    // 注册表:事件类型 → 观察者列表(线程安全)
    private final Map<String, List<Observer>> eventObservers = 
                            new ConcurrentHashMap<>();

    // 注册观察者到特定事件类型
    public void Add(String eventType, Observer observer) {
        eventObservers.computeIfAbsent(
            eventType, 
            k -> new CopyOnWriteArrayList<>()
        )
        .add(observer);
    }
}
// 日志观察者:仅处理 "LOG_EVENT" 事件
public class LogObserver implements Observer {
    @Override
    public void update(Event event) {
        if ("LOG_EVENT".equals(event.getType())) {
            System.out.println("日志记录: " + event.getData());
        }
    }
}

// 数据更新观察者:处理 "DATA_UPDATE" 事件
public class DataUpdateObserver implements Observer {
    @Override
    public void update(Event event) {
        if ("DATA_UPDATE".equals(event.getType())) {
            System.out.println("数据已更新: " + event.getData());
        }
    }
}

8)关系复杂时

当目标和观察者间的依赖关系特别复杂时,可能需要一个维护这些关系的对象。我们称这样的对象为更改管理器 (ChangeManager)。它的目的是尽量减少观察者反映其目标的状态变化所需的工作量。例如,如果一个操作涉及对几个相互依赖的目标进行改动,就必须保证仅在所有的目标都已更改完毕后,才一次性地通知它们的观察者,而不是每个目标都通知观察者。

ChangeManager有三个责任:

·它将一个目标映射到它的观察者并提供一个接口来维护这个映射。这就不需要由目标来维护对其观察者的引用,反之亦然。

·它定义一个特定的更新策略。

·根据一个目标的请求,它更新所有依赖于这个目标的观察者。

ChangeManager是Mediator模式的实例。通常只有一个ChangeManager并且全局可见。Singleton:天哪,这简直就是我!

实现放在Mediator里Mediator(中介者) --对象行为型模式-CSDN博客

10.代码示例

public abstract class Subject {
    // 用弱引用避免 Observer 永久被挂住
    private final Map<Observer, Set<EventType>> observers =
            new WeakHashMap<>();

    /** 注册:可指定多个感兴趣的事件类型,不传则表示对所有事件都感兴趣 */
    public void addObserver(Observer o, EventType... eventType) {
        Set<EventType> set =
                observers.computeIfAbsent(
                        o, k -> EnumSet.noneOf(EventType.class)
                );
        if (eventType.length == 0) { // 0 means all
            set.addAll(Arrays.asList(EventType.values()));
        } else {
            set.addAll(Arrays.asList(eventType));
        }
    }

    /** 退订:彻底取消所有事件 */
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    /** 模板方法:子类修改状态后都要调用它(或自动调用) */
    protected void notifyObservers(Event event) {
        // 1. 先快照,避免在迭代时 Observer 被移除
        List<Map.Entry<Observer,Set<EventType>>> snapshot =
                new ArrayList<>(observers.entrySet());
        for(var entry : snapshot) {
            Observer o = entry.getKey();
            Set<EventType> eventTypes = entry.getValue();
            // 2. 只通知感兴趣的 Observer
            if(eventTypes.contains(event.getEventType())) {
                // 3. 触发更新
                o.update(this, event);
            }
        }
    }

    // —— 以下是两种更新触发策略 ——

    /** 自动通知:每次修改后立即通知 */
    protected final void changeStateAuto(Runnable stateChanger, Object payload) {
        stateChanger.run();
        notifyObservers(new Event(EventType.STATE_CHANGE, payload));
    }

    /** 手动批量通知:调用者可以修改多次,最后一次性通知 */
    protected final void changeStateManual(Runnable stateChanger) {
        stateChanger.run();
        // 不自动通知,需外部显式调用 notifyObservers(...)
    }
}

public class ConcreteSubject extends Subject {
    private int state;

    public int getState() {
        return state;
    }

    /** 例:自动通知 */
    public void setStateAuto(int newState) {
        changeStateAuto(() -> this.state = newState, newState);
    }

    /** 例:手动通知 */
    public void setStateManual(int newState) {
        changeStateManual(() -> this.state = newState);
    }
    public void commit() {
        // 批量修改完毕后手动一次性通知
        notifyObservers(new Event(EventType.DATA_UPDATE, state));
    }
}
package Observer;

public interface Observer {
    /**
     * @param subject    触发更新的 Subject
     * @param event      事件类型 + 附加数据
     */
    void update(Subject subject, Event event);
}
package Observer;

import java.util.ArrayList;
import java.util.List;

public class ConcreteObserver implements Observer {
    private final List<Subject> subjects = new ArrayList<>();

    public ConcreteObserver(Subject... subs) {
        for (Subject s : subs) {
            // 注册时默认感兴趣的事件类型
            s.addObserver(this, EventType.STATE_CHANGE, EventType.DATA_UPDATE);
            subjects.add(s);
        }
    }

    @Override
    public void update(Subject subject, Event event) {
        // 拉模型:根据需要 pull 细节
        // instanceof用于检查一个对象是否是某个类(或其子类)的实例,或者是否实现了某个接口。
        if (subject instanceof ConcreteSubject) {
            int newState = ((ConcreteSubject) subject).getState();
            System.out.printf("Observer %s 收到 %s 的 %s 事件,state=%d%n",
                    this, subject, event.getEventType(), newState);
        }
        // 或者用推模型:Object payload = event.getPayload();
    }

    /** 在不再需要时,清理所有订阅,避免悬挂引用 */
    public void detachAll() {
        for (Subject s : subjects) {
            s.removeObserver(this);
        }
        subjects.clear();
    }
}
package Observer;
/**
 * 一个综合示例,演示:
 *  - 多 Subject 场景
 *  - 多事件类型过滤
 *  - 推/拉模型
 *  - 自动 vs 手动通知
 *  - 取消订阅
 *  - 自定义事件
 */
public class Client {
    public static void main(String[] args) {
        // --- 1. 创建多个 Subject ---
        ConcreteSubject subject1 = new ConcreteSubject();
        ConcreteSubject subject2 = new ConcreteSubject();

        // --- 2. 创建不同类型的 Observer ---
        // 2.1 只订阅 STATE_CHANGE 事件的 Observer
        Observer stateOnlyObserver = new Observer() {
            @Override
            public void update(Subject subject, Event event) {
                System.out.printf("[StateOnly] 收到 %s 的 %s,payload=%s, 直接拉取 state=%d\n",
                        subject, event.getEventType(), event.getPayload(),
                        (subject instanceof ConcreteSubject ? ((ConcreteSubject)subject).getState() : -1)
                );
            }
        };
        subject1.addObserver(stateOnlyObserver, EventType.STATE_CHANGE);

        // 2.2 只订阅 DATA_UPDATE 和 CUSTOM 事件的 Observer
        Observer dataOrCustomObserver = new Observer() {
            @Override
            public void update(Subject subject, Event event) {
                System.out.printf("[DataOrCustom] 收到 %s 的 %s,payload=%s\n",
                        subject, event.getEventType(), event.getPayload());
            }
        };
        subject1.addObserver(dataOrCustomObserver, EventType.DATA_UPDATE, EventType.CUSTOM);
        subject2.addObserver(dataOrCustomObserver, EventType.DATA_UPDATE, EventType.CUSTOM);

        // 2.3 使用已有的 ConcreteObserver,默认订阅 STATE_CHANGE + DATA_UPDATE
        ConcreteObserver multiObserver = new ConcreteObserver(subject1, subject2);

        System.out.println("--- 3. 自动通知 (Auto Notify) ---");
        subject1.setStateAuto(10);

        System.out.println("--- 4. 手动批量通知 (Manual Notify) ---");
        subject2.setStateManual(20);
        subject2.setStateManual(30);
        System.out.println("(还未通知,接下来手动 commit)");
        subject2.commit();

        System.out.println("--- 5. 自定义事件 (Custom Event) ---");
        // 直接用模板方法发送 CUSTOM 类型事件,并推送自定义 payload
        subject1.notifyObservers(new Event(EventType.CUSTOM, "CustomPayload123"));

        System.out.println("--- 6. 取消订阅 (Unsubscribe) ---");
        subject1.removeObserver(stateOnlyObserver);
        System.out.println("stateOnlyObserver 已退订 state_change,再次 setStateAuto(100) 只剩下其他两位了");
        subject1.setStateAuto(100);

        // 最后清理:detach 所有 multiObserver 对所有 Subjects 的订阅
        multiObserver.detachAll();
        subject1.removeObserver(dataOrCustomObserver);
        subject2.removeObserver(dataOrCustomObserver);
    }
}


11.相关模式

Mediator,Singleton

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值