设计模式 - 观察者模式(发布-订阅模式 or 模型-视图模式)

观察者模式(发布-订阅模式 or 模型-视图模式):被观察(触发器)对象状态改变时,则依赖于它的对象都会被通知并做出反应。
核心:依赖关系注册(一对多),事件产生时,发布人(主动)将事件通知到订阅人(被动)
角色:

  • 观察者(订阅人/被通知人):观察者(被动)接受到事件消息后,将做出反应。
  • 被观察者(发布人/通知者 - 触发器):定义通知事件,当被观察者的业务逻辑被执行时,会(主动)将对应事件发送给观察者
  • 订阅关系容器:维护事件与观察者之间的(一对多)联系(注册制),可能被集成于被观察者中,或者独立成类(此时,被观察者只负责产生事件,而由容器完成事件通知)

general(订阅关系由被观察者维护)

Observer

// 观察者,提供一个被通知方法(update - 回调方法)
public interface IObserver<E> {
    void update(E event);
}

public class Observer<E> implements IObserver<E>{
    @Override
    public void update(E event) {
        System.out.println("Observer:receive message : " + event);
    }
}

Subject

// 被观察者,提供注册和注销功能(维护该事件的观察者列表),以及事件触发方法notify,在notify中通知所有观察者。
public interface ISubject<E> {
    boolean register(IObserver<E> observer);
    boolean deregister(IObserver<E> observer);
    void notify(E event);
}

public class Subject<E> implements ISubject<E>{

    private ConcurrentLinkedQueue<IObserver> observers = new ConcurrentLinkedQueue<>();

    @Override
    public boolean register(IObserver<E> observer) { return observers.add(observer);}

    @Override
    public boolean deregister(IObserver<E> observer) { return observers.remove(observer);}

    @Override
    public void notify(E event) {
        IObserver[] iObservers = observers.toArray(new IObserver[observers.size()]);
        for(IObserver iObserver : iObservers){
            iObserver.update(event);
        }
    }
}
// 观察者模式(发布订阅模式):Observer需在Subject对象内进行注册,当发布消息时,Subject将消息提交给Observer。
// Subject对象中发布消息的功能,与维护Observer列表相耦合,可进一步拆分,实现单一职责。
public class Test {
    public static void main(String[] args) {
        Observer<String> observerA = new Observer<>();
        Observer<String> observerB = new Observer<>();
        Subject<String> subject = new Subject<>();
        subject.register(observerA);
        subject.register(observerB);

        subject.notify("字符串类型消息");
    }
}

publish/subscribe(发布和订阅关系的维护以及通知职责独立成context对象)

// 职责1:维护订阅关系。
// 职责2:接受到事件消息时,完成分发
public class Context {
    private ConcurrentHashMap<String, ConcurrentLinkedQueue<IUser>> subscribeMap = new ConcurrentHashMap<>();

    // 订阅
    public boolean subscribe(String publishUserId, IUser subscribeUser){
        if(!subscribeMap.containsKey(publishUserId)) {
            subscribeMap.putIfAbsent(publishUserId, new ConcurrentLinkedQueue<IUser>());
        }
        return subscribeMap.get(publishUserId).add(subscribeUser);
    }

    public boolean unsubscribe(String publishUserId, IUser subscribeUser){
        return subscribeMap.get(publishUserId).remove(subscribeUser);
    }

    // 分发
    public void publish(String userId, String message) {
        IUser[] iUsers = subscribeMap.get(userId).toArray(new IUser[subscribeMap.get(userId).size()]);
        for(IUser iUser : iUsers){
            iUser.subscribe(userId, message);
        }
    }
}
// 用户对象,只与context进行交流。
public class User {
    private String userId;
    private Context context;

    public User(String userId, Context context){
        this.userId = userId;
        this.context = context;
    }

    // 发布信息
    public void publish(String message){
        context.publish(userId,message);
    }

    // 订阅消息通知入口
    public void subscribe(String userId, String message) {
        System.out.println(this.userId + ": message from " + userId + " - " + message);
    }
}

Spring中集成的发布订阅模式

// 条件①:作为springbean对象被注册到applicationContext。
// 条件②:实现ApplicationListener<T extends ApplicationEvent>方法
// --- 即可作为观察者注册到applicationContext中,观察事件即为T对象事件
@Component
public class MyListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent myEvent) {
        System.out.println("MyListener - " + myEvent.getMessage());
    }
}
public class MyEvent extends ApplicationEvent {
    private String message;

    public MyEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() { return message;}

    public void setMessage(String message) { this.message = message;}
}
// Spring提供的事件监听(发布订阅)模式 - 采用SpringBootTest进行测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MySpringBootApplication.class)
public class MySpringBootApplicationTest {
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void test(){
        // 定义事件,并将事件发布到applicationContext中。
        // 具体的监听者在springBoot启动时,所有实现ApplicationListener<MyEvent>接口的spring bean对象均会注册到ApplicationEventMulticaster中,根据事件类型被通知调用
        MyEvent order = new MyEvent(this, "kiqi的消息");
        applicationContext.publishEvent(order);
     }
}
posted @ 2020-12-04 23:19  祁奇  阅读(165)  评论(0编辑  收藏  举报