zoukankan      html  css  js  c++  java
  • 设计模式

    观察者模式(发布-订阅模式 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);
         }
    }
    
  • 相关阅读:
    原生JS实现几个常用DOM操作API
    js字符串操作总结
    复选框 全选 反选 不选
    纯js实现分页
    JS 数据类型转换
    原生JS 购物车及购物页面的cookie使用
    js实现两种实用的排序算法——冒泡、快速排序
    linux下rename命令使用(可以实现文件批量重名)
    screen命令常用参数使用
    iptables端口转发规则(内网端口转外网端口)
  • 原文地址:https://www.cnblogs.com/kiqi/p/14088214.html
Copyright © 2011-2022 走看看