精华内容
下载资源
问答
  • 多观察的好处
    千次阅读
    2021-08-17 09:18:34





    一、观察者模式简介



    观察者模式 : 定义了 对象之间 一对多 的 依赖 , 令 多个 观察者 对象 同时 监听 某一个 主题对象 , 当 主题对象 发生改变时 , 所有的 观察者 都会 收到通知 并更新 ;

    观察者 有多个 , 被观察的 主题对象 只有一个 ;

    如 : 在购物网站 , 多个用户关注某商品后 , 当商品降价时 , 就会自动通知关注该商品的用户 ;

    • 主题对象 : 商品是主题对象 ;
    • 观察者 : 用户是观察者 ;
    • 观察者注册 : 用户关注 , 相当于注册观察者 ;
    • 通知触发条件 : 商品降价 ;

    观察者模式 类型 : 行为型 ;





    二、观察者模式适用场景



    观察者模式适用场景 : 关联行为 场景 , 建立一套 触发机制 ;

    如 : 用户关注某个商品的价格 , 降价时进行通知 , 这样 用户商品 产生了关联 , 触发机制就是 商品降价 ,





    三、观察者模式优缺点



    观察者模式 优点 :

    • 抽象耦合 :观察者被观察者 之间 , 建立了一个 抽象的 耦合 ; 由于 耦合 是抽象的 , 可以很容易 扩展 观察者 和 被观察者 ;
    • 广播通信 : 观察者模式 支持 广播通信 , 类似于消息广播 , 如果需要接收消息 , 只需要注册一下即可 ;

    观察者模式 缺点 :

    • 依赖过多 : 观察者 之间 细节依赖 过多 , 会增加 时间消耗程序的复杂程度 ;
      这里的 细节依赖 指的是 触发机制 , 触发链条 ; 如果 观察者设置过多 , 每次触发都要花很长时间去处理通知 ;
    • 循环调用 : 避免 循环调用 , 观察者 与 被观察者 之间 绝对不允许循环依赖 , 否则会触发 二者 之间的循环调用 , 导致系统崩溃 ;




    四、观察者模式代码示例



    JDK 中提供了观察者模式的支持 ;

    • 被观察者 : 被观察者 继承 Observable 类 ;
    • 观察者 : 观察者 实现 Observer 接口 ;
    • 关联 观察者 与 被观察者 : 调用 被观察者 的 addObserver 方法 , 关联二者 ;
    • 触发通知 : 被观察者 数据改变时 , 调用 setChangednotifyObservers 方法 , 会自动回调 观察者的 update 方法 ;

    用户在游戏中提出问题 , 管理员负责监听并处理问题 ;


    1、被观察者


    package observer;
    
    import java.util.Observable;
    
    /**
     * 被观察的主题对象
     *      JDK 提供了对观察者模式的支持 , 被观察者可以继承 Observable 类
     *
     * 被观察对象 继承 Observable 类
     */
    public class Game extends Observable {
        private String name;
    
        public Game(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        /**
         * 用户提交问题
         * @param game
         * @param question
         */
        public void produceQuestion(Game game, Question question) {
            System.out.println(question.getUserName() +
                    " 在 " + game.name + " 游戏中提交问题 : " + question.getContent());
    
            // 该方法是 Observable 提供的方法
            // 将 private boolean changed = false 成员变量设置为 true
            // 代表 被观察者 的状态发生了改变
            setChanged();
    
            // 通知 观察者
            notifyObservers(question);
        }
    }
    

    2、观察者


    package observer;
    
    import java.util.Observable;
    import java.util.Observer;
    
    /**
     * 管理员类
     *      管理员类观察的是游戏
     *      用户反馈的问题 属于 游戏 , 管理员关注的是游戏
     *      无法关注 问题
     *
     * 如 : 在电商平台 , 关注的是某个商品 , 在降价时发送通知
     *      商品是存在的 , 降价消息 在关注的时候还没有被创建 , 是无法获取依赖的
     */
    public class Manager implements Observer {
        /**
         * 管理员名称
         */
        private String name;
    
        public Manager(String name) {
            this.name = name;
        }
    
        @Override
        public void update(Observable o, Object arg) {
            // 获取 被观察者 对象
            Game game = (Game) o;
    
            // 获取 被观察者 对象 调用 notifyObservers 方法的参数
            Question question = (Question) arg;
    
            System.out.println(name + " 观察者 接收到 被观察者 " + game.getName() +
                    " 的通知 , 用户 " + question.getUserName() +
                    " 提出问题 " + question.getContent());
        }
    }
    

    3、通知类


    package observer;
    
    public class Question {
        /**
         * 用户名
         */
        private String userName;
    
        /**
         * 问题内容
         */
        private String content;
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    }
    

    4、测试类


    package observer;
    
    public class Main {
        public static void main(String[] args) {
            // 创建被观察者
            Game game = new Game("Cat And Mouse");
            // 创建观察者
            Manager manager = new Manager("Tom");
    
            // 关联 观察者 与 被观察者
            game.addObserver(manager);
    
            // 业务逻辑 : 用户提出问题到游戏中 , 管理员接收到通知消息
            Question question = new Question();
            question.setUserName("Jerry");
            question.setContent("游戏崩溃");
    
            // 在游戏中提交问题
            game.produceQuestion(game, question);
        }
    }
    

    执行结果 :

    JerryCat And Mouse 游戏中提交问题 : 游戏崩溃
    Tom 观察者 接收到 被观察者 Cat And Mouse 的通知 , 用户 Jerry 提出问题 游戏崩溃
    

    在这里插入图片描述





    五、JDK 中的观察者模式支持类




    1、Observable


    被观察者需要继承该类 ;

    public class Observable {
        private boolean changed = false;
        /** 使用 Vector 是线程安全的集合 , 存放观察者对象 , 在构造器中初始化该类 */
        private Vector<Observer> obs;
    
        /** Construct an Observable with zero Observers. */
    
        public Observable() {
            obs = new Vector<>();
        }
    
        /**
         * 添加观察者对象 , 采用了 synchronized 修饰 , 是同步方法 , 保证了线程安全 
         *
         * @param   o   an observer to be added.
         * @throws NullPointerException   if the parameter o is null.
         */
        public synchronized void addObserver(Observer o) {
            if (o == null)
                throw new NullPointerException();
            if (!obs.contains(o)) {
                obs.addElement(o);
            }
        }
    
        /**
         * 删除观察者 
         */
        public synchronized void deleteObserver(Observer o) {
            obs.removeElement(o);
        }
    
        /**
         * 通知观察者 , 无参
         *
         * @see     java.util.Observable#clearChanged()
         * @see     java.util.Observable#hasChanged()
         * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
         */
        public void notifyObservers() {
            notifyObservers(null);
        }
    
        /**
         * 通知观察者 , 有参
         *
         * @param   arg   any object.
         * @see     java.util.Observable#clearChanged()
         * @see     java.util.Observable#hasChanged()
         * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
         */
        public void notifyObservers(Object arg) {
            Object[] arrLocal;
    
            synchronized (this) {
                if (!changed)
                    return;
                arrLocal = obs.toArray();
                clearChanged();
            }
    
            for (int i = arrLocal.length-1; i>=0; i--)
                ((Observer)arrLocal[i]).update(this, arg);
        }
    
        public synchronized void deleteObservers() {
            obs.removeAllElements();
        }
    
        protected synchronized void setChanged() {
            changed = true;
        }
    
        protected synchronized void clearChanged() {
            changed = false;
        }
    
        public synchronized boolean hasChanged() {
            return changed;
        }
    
        /**
         * 查看观察者数量 
         *
         * @return  the number of observers of this object.
         */
        public synchronized int countObservers() {
            return obs.size();
        }
    }
    

    2、Observer


    观察者需要实现该接口 ;

    package java.util;
    
    /**
     * A class can implement the <code>Observer</code> interface when it
     * wants to be informed of changes in observable objects.
     *
     * @author  Chris Warth
     * @see     java.util.Observable
     * @since   JDK1.0
     */
    public interface Observer {
        /**
         * 当 被观察者 对象发生改变时 , 即被观察者对象调用 notifyObservers 方法时 , 自动调用该方法
         *
         * @param   o     被观察的对象.
         * @param   arg   被观察对象调用 notifyObservers 方法时传入的参数 
         */
        void update(Observable o, Object arg);
    }
    
    
    更多相关内容
  • 观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对的关系,让观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。...
  • 观察员中心基于 ,在 obj-c 中为多观察者模式创建一个观察者中心。超越 PLObservers 的优势将被观察者和被观察者解耦; 订阅指定键的新功能; 使用更方便。用法 1. without keyADD_OBSERVER(protocol, observer)...
  • 评估来曲唑在不育妇女囊卵巢综合征中诱导排卵的有效性和益处。 方法:回顾性分析我院2016年6月至2018年6月转诊至长江大学第一附属医院生殖内科的242例第一排卵诱导周期PCOS患者的临床资料,随机分为来曲唑组和...
  • 使用观察者模式的好处: 支持简单的广播通信,自动通知所有已经订阅过的对象。 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。 ...
  • 观察者模式(发布-订阅模式):其定义对象间一种一对的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。 在JavaScript中,一般使用事件模型来替代传统的观察者模式。 好处: (1)可...
  • 中国观察:中国的“生产率悖论”与企业有效破产的好处.pdf
  • 观察者模式及其应用场景

    千次阅读 2022-04-24 08:18:02
    指在对象之间定义一个一对的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。 比如说Redis 中的基于频道的发布订阅就是观察者模式的应用: 一、观察者模式的介绍 观察者模式是一种对象行为型...

    观察者模式(Observer Design Pattern),也叫做发布订阅模式(Publish-Subscribe Design Pattern)、模型-视图(Model-View)模式、源-监听器(Source-Listener)模式、从属者(Dependents)模式。指在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。

    比如说Redis 中的基于频道的发布订阅就是观察者模式的应用:

    image-20220212142801417

    一、观察者模式的介绍

    观察者模式是一种对象行为型模式,下面就来看看观察者模式的结构及其实现:

    1.1 观察者模式的结构

    观察者模式结构中主要包括观察目标(Object)和观察者(Observer)主要结构:

    image-20220409112301928

    • Subject:主题抽象类,提供一系列观察者对象,以及对这些对象的增加、删除和通知的方法
    • ConcreteSubject:主题具体实现类,实现抽象主题中的通知方法,通知所有注册过的观察者对象
    • Observer:观察者抽象类,包含一个通知响应抽象方法
    • ConcreteObserver1、ConcreteObserver2:观察者实现类,实现抽象观察者中的方法,以便在得到目标的更改通知时更新自身的状态
    • Client:客户端,对主题及观察者进行调用

    1.2 观察者模式的实现

    根据上面的类图,我们可以实现对应的代码。

    首先定义一个抽象目标类Subject,其中包括增加、注销和通知观察者方法

    public abstract class Subject {
    
        protected List<Observer> observerList = new ArrayList<Observer>();
    
        /**
         * 增加观察者
         * @param observer 观察者
         */
        public void add(Observer observer) {
            observerList.add(observer);
        }
    
        /**
         * 注销观察者,从观察者集合中删除一个观察者
         * @param observer 观察者
         */
        public void remove(Observer observer) {
            observerList.remove(observer);
        }
    
        /**通知观察者*/
        public abstract void notifyObserver();
    }
    

    对应具体的目标类ConcreteSubject

    public class ConcreteSubject extends Subject{
    
        @Override
        public void notifyObserver() {
            System.out.println("遍历观察者:");
            for (Observer observer : observerList) {
                observer.response();
            }
        }
    }
    

    此外需要定义抽象观察者Observer,它一般定义为一个接口,声明一个response()方法,为不同观察者的响应行为定义相同的接口:

    public interface Observer {
        /**声明响应方法*/
        void response();
    }
    

    具体的观察者实现:

    public class ConcreteObserver1 implements Observer{
    
        @Override
        public void response() {
            System.out.println("我是具体观察者ConcreteObserver1");
        }
    }
    
    public class ConcreteObserver2 implements Observer{
    
        @Override
        public void response() {
            System.out.println("我是具体观察者ConcreteObserver2");
        }
    }
    

    最后是客户端测试:

    public class Client {
        public static void main(String[] args) {
            Subject concreteSubject = new ConcreteSubject();
            //具体观察者
            Observer concreteObserver1 = new ConcreteObserver1();
            Observer concreteObserver2 = new ConcreteObserver2();
            concreteSubject.add(concreteObserver1);
            concreteSubject.add(concreteObserver2);
            
            concreteSubject.notifyObserver();
        }
    }
    

    测试结果:

    遍历观察者:
    我是具体观察者ConcreteObserver1
    我是具体观察者ConcreteObserver2
    

    二、观察者模式的应用场景

    在以下情况就可以考虑使用观察者模式:

    1. 一个对象的改变会导致一个或多个对象发生改变,而并不知道具体有多少对象将会发生改变,也不知道这些对象是谁
    2. 当一个抽象模型有两个方面,其中的一个方面依赖于另一个方面时,可将这两者封装在独立的对象中以使他们可以各自独立地改变和复用
    3. 需要在系统中创建一个触发链,使得事件拥有跨域通知(跨越两种观察者的类型)

    2.1 观察者模式在java.util包中的应用

    观察者模式在JDK中就有典型应用,比如java.util.Observablejava.util.Observer类。结构如下图所示:

    image-20220409083948434

    我们可以通过实现具体的ConcreteObserver和具体的ConcreteObservable完成观察者模式流程

    2.2 观察者模式在MVC中的应用

    MVC(Modew-View-Controller)架构中也应用了观察者模式,其中模型(Model)可以对应观察者模式中的观察目标,而视图(View)对应于观察者,控制器(Controller)就是中介者模式的应用:

    image-20220409091533004

    三、观察者模式实战

    在本案例中模拟北京小客车指标摇号事件的通知场景(来源于《重学Java设计模式》)

    image-20220409092520707

    对于通知事件,可以将其分成三个部分:事件监听事件处理具体的业务流程,如下图所示:

    image-20220409095032686

    对于和核心流程和非核心流程的结构,非核心流程可以是异步的,在MQ以及定时任务的处理下,能够最终保证一致性。

    具体代码实现

    1. 事件监听接口及具体实现

    这个部分就相当于观察者(Observer)的角色

    在接口中定义基本事件类方法doEvent()

    public interface EventListener {
    
        void doEvent(LotteryResult result);
    
    }
    

    监听事件的具体实现MessageEventListener(短消息事件)和MQEventListener(MQ发送事件)

    public class MessageEventListener implements EventListener{
    
        private Logger logger = LoggerFactory.getLogger(MessageEventListener.class);
    
        @Override
        public void doEvent(LotteryResult result) {
            logger.info("给用户 {} 发送短信通知(短信):{}", result.getuId(), result.getMsg());
        }
    }
    
    public class MQEventListener implements EventListener{
    
        private Logger logger = LoggerFactory.getLogger(MQEventListener.class);
    
        @Override
        public void doEvent(LotteryResult result) {
            logger.info("记录用户 {} 摇号结果(MQ):{}", result.getuId(), result.getMsg());
        }
    }
    
    1. 事件处理类

    该部分就相当于主题(Object)部分

    对于不同的事件类型(MQ和Message)进行枚举处理,并提供三个方法:subscribe()unsubscribe()notify()用于对监听事件的注册和使用:

    public class EventManager {
    
        Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();
    
        public EventManager(Enum<EventType>... operations) {
            for (Enum<EventType> operation : operations) {
                listeners.put(operation, new ArrayList<>());
            }
        }
    
        public enum EventType {
            MQ,
            Message
        }
    
        /**
         * 订阅
         * @param eventType 事件类型
         * @param listener  监听
         */
        public void subscribe(Enum<EventType> eventType, EventListener listener) {
            List<EventListener> eventListeners = listeners.get(eventType);
            eventListeners.add(listener);
        }
    
        /**
         * 取消订阅
         * @param eventType 事件类型
         * @param listener 监听
         */
        public void unsubscribe(Enum<EventType> eventType, EventListener listener) {
            List<EventListener> eventListeners = listeners.get(eventType);
            eventListeners.remove(listener);
        }
    
        /**
         * 通知
         * @param eventType 事件类型
         * @param result    结果
         */
        public void notify(Enum<EventType> eventType, LotteryResult result) {
            List<EventListener> eventListeners = listeners.get(eventType);
            for (EventListener eventListener : eventListeners) {
                eventListener.doEvent(result);
            }
        }
    }
    
    1. 业务抽象类接口及其实现

    使用抽象类的方式实现方法,好处是可以在方法中扩展额外的调用,并提供抽象方法doDraw,让继承者去实现具体逻辑

    public abstract class LotteryService {
    
        private EventManager eventManager;
    
        public LotteryService() {
            eventManager = new EventManager(EventManager.EventType.MQ, EventManager.EventType.Message);
            eventManager.subscribe(EventManager.EventType.MQ, new MQEventListener());
            eventManager.subscribe(EventManager.EventType.Message, new MessageEventListener());
        }
    
        public LotteryResult draw(String uId) {
            LotteryResult lotteryResult = doDraw(uId);
            eventManager.notify(EventManager.EventType.MQ, lotteryResult);
            eventManager.notify(EventManager.EventType.Message, lotteryResult);
            return lotteryResult;
        }
    
        protected abstract LotteryResult doDraw(String uId);
    }
    
    public class LotteryServiceImpl extends LotteryService{
    
        private MinibusTargetService minibusTargetService = new MinibusTargetService();
    
        @Override
        protected LotteryResult doDraw(String uId) {
            //摇号测试
            String lottery = minibusTargetService.lottery(uId);
            return new LotteryResult(uId, lottery, new Date());
        }
    }
    
    1. 其他的类

    摇号服务接口:

    /**
     * 小客车指标调控服务
     */
    public class MinibusTargetService {
    
        /**
         * 模拟摇号,但不是摇号算法
         *
         * @param uId 用户编号
         * @return 结果
         */
        public String lottery(String uId) {
            return Math.abs(uId.hashCode()) % 2 == 0 ? "恭喜你,编码".concat(uId).concat("在本次摇号中签") : "很遗憾,编码".concat(uId).concat("在本次摇号未中签或摇号资格已过期");
        }
    
    }
    

    事件信息返回类:

    public class LotteryResult {
    
        private String uId;
        private String msg;
        private Date dateTime;
    
        //get set constructor... 
    }
    
    1. 测试类
    public class ApiTest {
    
        private Logger logger = LoggerFactory.getLogger(ApiTest.class);
    
        @Test
        public void test() {
            LotteryServiceImpl lotteryService = new LotteryServiceImpl();
            LotteryResult result = lotteryService.draw("1234567");
            logger.info("摇号结果:{}", JSON.toJSONString(result));
        }
    }
    

    测试结果:

    11:43:09.284 [main] INFO  c.e.d.event.listener.MQEventListener - 记录用户 1234567 摇号结果(MQ):恭喜你,编码1234567在本次摇号中签
    11:43:09.288 [main] INFO  c.e.d.e.l.MessageEventListener - 给用户 1234567 发送短信通知(短信):恭喜你,编码1234567在本次摇号中签
    11:43:09.431 [main] INFO  ApiTest - 摇号结果:{"dateTime":1649475789279,"msg":"恭喜你,编码1234567在本次摇号中签","uId":"1234567"}
    

    参考资料

    《重学Java设计模式》

    《设计模式》

    http://c.biancheng.net/view/1390.html

    展开全文
  • 前面使用纯php实现了一个观察者模式(php观察者模式), 现在使用php标准库spl在次实现观察者模式,好处是:随意的生成您想使用的观察者! <?php /** * Created by PhpStorm. * User: evolution * Date: 14-...
  • 前言 无论大家在实践中是否自己实现过观察者模式或监听器模式,但肯定间接使用过。...这我们就要说说设计模式的好处了。设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、

    前言

    无论大家在实践中是否自己实现过观察者模式或监听器模式,但肯定间接使用过。比如Spring的事件机制,大多数人肯定都用过,只是没留意而已。

    今天这篇文章主要围绕观察者模式、监听器模式,以及它们之间的关系展开。不仅用实例介绍它们的使用,而且也会聊一聊Spring事件机制对观察者模式的实践。

    监听器模式和观察者模式怎么看起来是一样的?

    先聊聊设计模式

    为什么要使用监听模式,直接调用不好吗?这我们就要说说设计模式的好处了。设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。同时,采用设计模式之后,代码能够达到低耦合、低依赖的效果。

    也就是说,即便不用设计模式,直接硬编码也能实现。但如果考虑到代码的耦合性、依赖性、扩展性等问题,设计模式便是更好的选择。比如观察者模式就能达到解耦、异步等效果。

    观察者模式的定义

    观察者模式定义了对象之间的一对多依赖,即一个主题对应多个观察者。当一个主题对象改变状态时,它的所有依赖者(观察者)都会收到通知并自动更新。比如用户订阅某个订阅号或公众号,当发新消息时,会发送给所有订阅者。

    image

    观察者模式解决的是对象和对象之间的依赖关系。当多个对象依赖一个对象的关系时,一个主题对象状态改变,需要通知所有观察者对象。

    监听器模式并不是一个新的设计模式,它是观察者模式在特定场景下的一种改造和应用。通常,观察者模式的主题在通知观察者时,通知中不包含任何信息。如果这个过程中携带了一些其他信息,那么主题本身就成为了事件源,而携带信息的封装类就成为了事件。此时的观察者模式,也就升级为监听器了。监听器模式是观察者模式的另一种形态。

    观察者模式实例

    先来看看观察者模式的代码实现,可以直接使用JDK自带的Observer,也可以自定义对应的API。单从JDK自带观察者模式的API,也可以看出该设计模式的分量(虽然在Java被废弃了)。

    我们这里采用自定义相关类,主要包括主题和观察者两种对象。

    首先定义一个主题对应的接口Subject:

    public interface Subject {
    
        /**
         * 注册定义
         */
        void registerObserver(Observer observer);
    
        /**
         * 发送通知
         */
        void notifyObservers(Object msg);
    
    }
    

    主题接口中定义了两个方法一个用来注册观察者,一个用来发送通知。定义这个主题的具体实现类ConcreteSubject:

    public class ConcreteSubject implements Subject {
    
        /**
         * 观察者集合
         */
        private List<Observer> observers = new ArrayList<>();
    
        @Override
        public void registerObserver(Observer observer) {
            // 添加订阅关系
            observers.add(observer);
        }
    
        @Override
        public void notifyObservers(Object msg) {
            // 通知订阅者
            for (Observer observer : observers) {
                observer.update(msg);
            }
        }
    }
    

    实现类中存储了,观察者的集合,这样就实现了主题和观察者之间一对多的关系。

    创建一个观察者接口Observer,方便管理:

    public interface Observer {
        // 处理业务逻辑
        void update(Object msg);
    }
    

    定义观察者接口的具体实现类ConcreteObserver:

    public class ConcreteObserver implements Observer {
    
        @Override
        public void update(Object msg) {
            // 业务逻辑实现
            System.out.println("ConcreteObserver 接收到主题的消息: " + msg);
        }
    }
    

    在实现类中,打印一行消息。当然,在实践的过程中,这个实现类可以通过匿名类的形式创建,这样就具体的匿名类就在registerObserver时定义了。

    下面来测试一下:

    public class ObserverTest {
    
        public static void main(String[] args) {
            Subject subject = new ConcreteSubject();
            Observer observer = new ConcreteObserver();
            // 注册观察者
            subject.registerObserver(observer);
            // 发布消息
            subject.notifyObservers("来自Subject的消息");
        }
    }
    

    执行程序,打印结果:

    ConcreteObserver 接收到主题的消息: 来自Subject的消息
    

    说明可以正常接收到主题发布的消息。

    在上面的实现中,可以看出已经达到了解耦合的效果,同时减少了依赖关系。每个观察者根本不需要知道发布者处理了什么业务逻辑,也不依赖于发布者的业务模型,只关心自己的逻辑处理即可。

    监听模式实例

    监听器模式通常包含三个角色:事件源、事件对象、事件监听器。如果观察者模式中的类名和方法对照改一下,并不改变业务逻辑,我们来看看是啥效果。

    比如,将Observer改名为Listener,将其update方法改为onClick();将Subject的实现类ConcreteSubject改名为ListenerSupport,将registerObserver方法更名为addListener……

    定义一个事件对象Event,用来传递事件信息:

    public class Event {
        private String data;
        private String type;
    
        Event(String data, String type) {
            this.data = data;
            this.type = type;
        }
        // 省略getter/setter
    }
    

    原来主题的订阅对象Observer改名为Listener之后,成为监听器:

    public interface Listener {
        void onClick(Event event);
    }
    

    为监听器提供一个实现类,当然在实践中也可以采用匿名类创建的方式:

    public class ListenerA implements Listener {
    
        @Override
        public void onClick(Event event) {
            System.out.println("触发事件,type:" + event.getType() + ",data:" + event.getData());
        }
    }
    

    原来的主题ConcreteSubject对照ListenerSupport成为事件管理器:

    public class ListenerSupport {
    
        private List<Listener> listeners = new ArrayList<>();
    
        public void addListener(Listener listener) {
            listeners.add(listener);
        }
    
        public void triggerEvent(Event event) {
            for (Listener listener : listeners) {
                listener.onClick(event);
            }
        }
    }
    

    对应的测试代码:

    public class EventTest {
    
        public static void main(String[] args) {
            Listener listener = new ListenerA();
            ListenerSupport listenerSupport = new ListenerSupport();
            listenerSupport.addListener(listener);
            listenerSupport.triggerEvent(new Event("dataA", "typeA"));
        }
    }
    

    执行程序,打印信息如下:

    触发事件,type:typeA,data:dataA
    

    通过上面的对照代码,我们可以看出,即便业务逻辑不变,经过重命名的观察者模式已经变为监听器模式了。而它们的对照关系是:事件源对照ConcreteSubject(主题)、事件对象对照update方法的Object、事件监听器对照ConcreteObserver(订阅者)。这也再次证明了所说的“监听器模式是观察者模式的另一种形态”。

    观察者模式和监听器模式对比

    用一张图,来比较观察者模式和监听器模式的联系和区别:
    image

    通过对比可以发监听器模式的优势是:在很多场景中,通知中附带了一些必不可少的其他信息,而事件Event可以对这些信息进行封装,使它本身拥有了多态的特性。每个事件对象就可以包含不同的信息。从这个层面来说,事件监听器模式是对观察者模式进行了进一步的抽象。

    Spring中的最佳实践

    观察者模式的经典应用算是Spring事件驱动模型了,它便是基于观察者模式实现的,同时也是项目中最常见的事件监听器。

    Spring中观察者模式包含四个角色:事件、事件监听器、事件源、事件管理。

    事件:ApplicationEvent是所有事件对象的父类。ApplicationEvent继承自jdk的EventObject,所有的事件都需要继承ApplicationEvent,并且通过source得到事件源。Spring 提供了很多内置事件,比如:ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent。

    事件监听器:ApplicationListener,也就是观察者,继承自jdk的EventListener,该类中只有一个方法onApplicationEvent,当监听的事件发生后该方法会被执行。

    事件源:ApplicationContext,ApplicationContext是Spring的核心容器,在事件监听中ApplicationContext可以作为事件的发布者,也就是事件源。因为ApplicationContext继承自ApplicationEventPublisher。在ApplicationEventPublisher中定义了事件发布的方法:publishEvent(Object event)。

    事件管理:ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把Applicationcontext发布的Event广播给它的监听器列表。

    小结

    通过本篇文章我们知道,监听器模式的本质就是观察者模式,先将回调函数注册到被观察对象,当被观察对象发生变化时,通过回调函数告知观察者/监听者。而Spring中事件管理也是基于观察者模式实现的,算是一个比较经典的案例。


    程序新视界

    公众号“ 程序新视界”,一个让你软实力、硬技术同步提升的平台,提供海量资料

    微信公众号:程序新视界

    展开全文
  • 设计模式(二)————观察者模式

    千次阅读 多人点赞 2021-10-15 16:20:31
    定义对象间一种一对的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新 从定义就可以看出,观察者模式主要就是两部分:主题(Subject)和观察者(Observer) 为了更清楚的了解...

    引言

    观察者模式可以说是JDK中使用最多的模式之一,这个模式可以让你的对象随时随地的了解想要知道的情况,你还可以让某个对象在运行时决定是否要继续通知;观察者模式的定义为:

    定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新

    从定义就可以看出,观察者模式主要就是两部分:主题(Subject)和观察者(Observer)

    为了更清楚的了解观察者模式,下面将会用案例说明观察者模式;

    天气预报项目

    今天一个新的项目交到了你的手上,这个项目是一个天气预报公司的,他们想要

    • 将每天测量到的温度,湿度,气压等等以公告的形式发布出去
    • 设计开放型 API,便于其他第三方也能接入气象站获取数据
    • 而且天气预报最基本的要求是准时,不能开始下雨了才把下雨的预报发出来,所以很重要的一点是:每当天气预报更新的时候,要实时通知给第三方;

    通过需求分析可以提炼出关键一点:公布天气信息的“布告板”可以有多个,这些布告板全部从天气预报公司获取天气信息;
    大致是下图的意思:
    在这里插入图片描述
    再加上实时更新的需求,就是对标观察者模式;
    上面图可以等同于下面这个:
    在这里插入图片描述
    所以只需要确定好观察者模式的两个主要组成:主题(Subject)观察者(Observer)就好了;

    很明显,主题对象就是天气预报公司,观察者就是一系列第三方网站(的布告板)

    接下来就是如何用方法将观察者和主题联系起来呢?
    对于观察者来说,需要的方法就是一个:

    • 接受主题的输入(update)

    对于主题来说,需要以下功能:

    • registerObserver() 注册
    • removeObserver() 移除
    • notifyObservers() 通知注册的用户

    那么就可以简单尝试画出UML类图了:
    在这里插入图片描述
    大致就是这样,Subject是主题的接口,Observer是观察者的接口,
    可能你会觉得上面明明就说的那么简单,怎么这一下子多了好几个东西
    其实这样子的代码耦合度更低,而且思路非常简单,下面就来通过代码实现一下就明白了;


    主题:

    // 主题接口
    public interface Subject {
        public void registerObserver(Observer o); // 注册观察者
        public void removeObserver(Observer o); // 移除观察者
        public void notifyObserver(); // 通知观察者
    }
    
    // 实现主题接口,该类为天气预报公司
    import java.util.ArrayList;
    
    public class WeatherData implements Subject {
        private ArrayList<Observer> observers; // 观察者数组,存放所有观察者
        private float temperature; // 温度
        private float humidity; // 湿度
        private float pressure; // 压力
        public WeatherData() {
            observers = new ArrayList<>();
        }
        // 注册观察者
        @Override
        public void registerObserver(Observer o) {
            observers.add(o);
        }
        // 移除观察者
        @Override
        public void removeObserver(Observer o) {
            int i = observers.indexOf(o);
            if (i >= 0) {
                observers.remove(o);
            }
        }
        // 通知观察者
        @Override
        public void notifyObserver() {
            for (var i : observers) {
                i.update(this.temperature, this.humidity, this.pressure);
            }
        }
        // 信息更新
        public void measurementsChanged() {
            notifyObserver();
        }
        // 设置天气
        public void setMeasurements(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChanged(); // 设置完成天气就已经改变了
        }
    }
    


    观察者:

    // 观察者
    public interface Observer {
        public void update(float temp, float humidity, float pressure); // 更新布告板
        public void display(); // 展示布告板
    }
    
    // 天气预报公司自己的布告板
    public class CurrentConditionsDisplay implements Observer{
        private float temperature; // 温度
        private float humidity; // 湿度
        private float pressure; // 压力
        @Override
        public void update(float temp, float humidity, float pressure) {
            this.temperature = temp;
            this.humidity = humidity;
            this.pressure = pressure;
            display();
        }
    
        @Override
        public void display() {
            System.out.println("当前温度:" + this.temperature +
                    "当前湿度:" + this.humidity + "当前压力:" + this.pressure);
        }
    }
    
    // 百度网站的温度布告板
    public class BaiDuDisplay implements Observer{
        private float temperature; // 温度
        private float humidity; // 湿度
        private float pressure; // 压力
        @Override
        public void update(float temp, float humidity, float pressure) {
            this.temperature = temp;
            this.humidity = humidity;
            this.pressure = pressure;
            display();
        }
        @Override
        public void display() {
            System.out.println("百度温度:" + this.temperature +
                    "百度湿度:" + this.humidity + "百度压力:" + this.pressure);
        }
    }
    

    还可以继续增加布告板,这里就列举这两个了;

    测试执行代码:

    public class WeatherStation {
        public static void main(String[] args) {
            WeatherData weatherData = new WeatherData();
            Observer current = new CurrentConditionsDisplay(); // 自己网站的观察者
            Observer baidu = new BaiDuDisplay(); // 百度观察者
            weatherData.registerObserver(current); // 注册观察者
            weatherData.registerObserver(baidu); // 注册观察者
            weatherData.setMeasurements(11, 22, 33);
        }
    }
    

    运行结果:

    当前温度:11.0当前湿度:22.0当前压力:33.0
    百度温度:11.0百度湿度:22.0百度压力:33.0
    

    其实一看代码,还是两大部分,所以对于观察者模式,主要思考的是主题和观察者的关系就可以了;

    使用了观察者模式后,代码有以下好处:

    • 观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知
    • 增加观察者(即成一个新的公告板),就不需要去修改核心类 WeatherData 的代码,遵守 ocp (开闭)原则

    Java内置观察者模式

    JDK中提供了 java.util.Observable 实现类和 java.util.Observer 接口,这是Java内置的观察者模式,可以直接来用,先简单对比介绍一下:

    • Observable 的作用和地位等价于前面的Subject
    • Observable 是类,不是接口,类中已经实现了核心的方法 ,即管理 Observer 的方法 add… delete … notify…
    • Observer 接口的作用等价于前面的 Observer接口, 都有update
    • Observable类 和 Observer接口 的使用方法和前面讲过的一样,只是 Observable 是类,通过继承来实现观察者模式

    下面我将用内置观察者模式支持来重写一下上面的代码实现相同的功能;

    主题:

    import java.util.Observable;
    
    // 直接继承Observable类
    public class WeatherData extends Observable {
        private float temperature; // 温度
        private float humidity; // 湿度
        private float pressure; // 压力
        // 温度改变通知观察者
        public void measurementsChanged() {
            setChanged(); // 确定已经改变
            notifyObservers();
        }
        // 设置温度
        public void setMeasurements(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChanged();
        }
        public float getTemperature() {
            return temperature;
        }
        public float getHumidity() {
            return humidity;
        }
        public float getPressure() {
            return pressure;
        }
    }
    


    观察者:

    // 天气预报公司自己的布告板
    import java.util.Observable;
    import java.util.Observer;
    
    // 实现了Observer接口
    public class CurrentConditionsDisplay implements Observer{
        private float temperature; // 温度
        private float humidity; // 湿度
        private float pressure; // 压力
        public void display() {
            System.out.println("当前温度为:" + this.temperature + " 当前湿度为:"
                    + this.humidity + " 当前压力为:" + this.pressure);
        }
        @Override
        public void update(Observable o, Object arg) {
            if (o instanceof WeatherData) {
                WeatherData weatherData = (WeatherData) o;
                this.temperature = weatherData.getTemperature();
                this.humidity = weatherData.getHumidity();
                this.pressure = weatherData.getPressure();
                display();
            }
        }
    }
    
    // 百度网站的温度布告板
    import java.util.Observable;
    import java.util.Observer;
    
    // 实现了Observer接口
    public class BaiDuDisplay implements Observer {
        private float temperature; // 温度
        private float humidity; // 湿度
        private float pressure; // 压力
        public void display() {
            System.out.println("百度温度为:" + this.temperature + " 百度湿度为:"
                    + this.humidity + " 百度压力为:" + this.pressure);
        }
        @Override
        public void update(Observable o, Object arg) {
            if (o instanceof WeatherData) {
                WeatherData weatherData = (WeatherData) o;
                this.temperature = weatherData.getTemperature();
                this.humidity = weatherData.getHumidity();
                this.pressure = weatherData.getPressure();
                display();
            }
        }
    }
    

    测试执行代码:

    public class WeatherStation {
        public static void main(String[] args) {
            WeatherData weatherData = new WeatherData(); // 主题
            CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(); // 观察者
            BaiDuDisplay baiDuDisplay = new BaiDuDisplay(); // 百度观察者
            weatherData.addObserver(currentConditionsDisplay); // 增添观察者
            weatherData.addObserver(baiDuDisplay);// 增添观察者
            weatherData.setMeasurements(11, 22, 33);
        }
    }
    

    运行结果:

    百度温度为:11.0 百度湿度为:22.0 百度压力为:33.0
    当前温度为:11.0 当前湿度为:22.0 当前压力为:33.0
    

    是不是有很多相同的地方,归根结底都是使用的观察者模式,所以只要知道观察者模式由什么组成,什么时候用,那么你就真正了解了观察者模式;

    但是设计模式可不是随便看看这俩代码就能会用的,这也是设计模式不好学的一点,还是需要大量的代码练习去体会,最后就能灵活运用;

    注:在JDK中JavaBeans和Swing都实现了观察者模式,感兴趣可以尝试一下;

    总结

    总结一下观察者模式:

    • 观察者模式定义了对象之间一对多的关系
    • 主题通过一个共同的接口来更新观察者
    • 观察者和主题之间用松耦合的方式结合,主题不知道观察者的细节,只知道观察者实现了观察者接口
    • 当有多个观察者时,不能依赖特定的通知次序

    最后在这里展示一下观察者模式的UML类图:
    在这里插入图片描述

    在这篇文章中的例子的代码只是为了展示观察者模式,但是并不是最优的代码,一个好的代码可能要用到多种设计模式,这也是设计模式的一个难点;
    设计模式的学习没有什么技巧,只能在敲代码的过程中不断去体会,可能某一天你就会不自觉的使用设计模式,还是要多敲代码多踩坑多思考,希望我们一起努力!!

    展开全文
  • 观察法的主要特点有哪些

    千次阅读 2021-02-12 11:14:11
    运用教育观察法往往不需要专业程度很高因而较难掌握的理论和技术,这32313133353236313431303231363533e59b9ee7ad9431333365656535样,无论是常规工作中为了改进现状所作的探索,还是专业研究人员所进行的预备研究...
  • Observ为有状态应用程序提供了以下两个好处: 您不再需要手动使状态无效并重新计算状态(例如,通过脏标志): 计算状态自动失效 懒惰地重新计算了计算状态 您可以对状态变化(无论是否计算)做出React,从而启用...
  • 说说zookeeper的观察

    2019-08-07 14:49:49
    想写这么一个文章呢,是源于某天,有人问我,你知道zookeeper的观察者吗?作为一个大数据攻城狮,那肯定是知道的呀。于是灵魂三问来了,那你知道为什么要有zookeeper的观察者吗,你知道观察者的作用吗,你知道观察者...
  • 观察者模式的优缺点

    千次阅读 2020-11-09 19:13:45
    1、观察者和被观察者之间建立了一个抽象的耦合。 2、观察者模式支持广播通信。 缺点: 1、观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。 2、使用要得当,要避免循环调用。 ...
  • 4.具体观察者(ConcretedObserver)角色(个):存储一个具体主题对象,存储相关状态,实现抽象观察者角色所要求的更新接口,以使得其自身状态和主题的状态保持一致。   【观察者模式中主要角色关系】   ...
  • 几种观察者模式

    2017-12-10 17:44:31
    定义:观察者模式(有时又被称为发布-订阅模式、模型-视图模式、源-收听者模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出...
  • 简述观察法的主要优点和不足。

    千次阅读 2021-04-28 07:40:35
    简述观察法的主要优点和不足。更相关问题屋根で石を投げました。Listen to the dialogue again, and then choose the best answer. 1. The nearest branch is on ____最常见的瓣膜病类型是 (5.0分)[选题,4分] ...
  • 改进方案---观察者模式 在普通方案的基础上进行改进,新增subject接口和observer接口,subject接口用来管理个奶站或者气象站的信息,将信息发布出去。 observer接口则可以用来给个第三方接入来获取奶站或者气象...
  • 设计模式——观察者模式

    千次阅读 2021-12-28 15:40:32
    本文简单介绍了关于设计模式的一种,观察者模式
  • 在这种情势下,以LED(发光二极管)技术制造的观察片装置成为一种趋势,由于LED具有体积小、寿命长、驱动电压低、耗电量低、无闪烁、反应速度快、耐震性佳等优点,它给医务人员观片带来了很大的好处,可以大大减少因...
  • 观察者模式的定义: 观察者模式指的是一个对象(Subject)维持一系列依赖于它的对象(Observer),当有关状态发生变更时 Subject 对象则通知一系列 Observer 对象进行更新。 简单来说就是 A 对 B 的某个属性敏感,...
  • 观察者模式,根据应用场景的不同,观察者模式会对应不同的代码实现方式:有同步阻塞的实现方式,也有异步非阻塞的实现方式;有进程内的实现方式,也有跨进程的实现方式。今天我会重点讲解原理、实现、应用场景。下一...
  • Zookeeper--观察

    多人点赞 2021-01-30 11:35:40
    zookeeper的观察者详解 - 知乎 《从Paxos到Zookeeper 分布式一致性原理与实践》=>7.7.3 Observer 简介 观察者的设计是希望能动态扩展zookeeper集群又不会降低写性能。 虽然通过让客户端直接连接到集群的投票...
  • 好处包括: 使用块,自定义操作或NSKeyValueObserving回调的通知。 移除观察者也没有例外。 控制器dealloc上的隐式观察者删除。 具有防止观察者复活的特殊防护的线程安全– 。 有关KVO的更信息,请参阅Apple...
  • 一、何为观察者模式? 观察者模式(有时又被称为发布/订阅模式)是软体设计模式的一种。在此种模式中,一个被观察对象管理所有...二、使用它它有何好处观察者模式(Observer)完美的将观察者和被观察的对象分离开
  • 在项目中应用“观察者模式”清除层层if...else if...的臃肿代码 文章目录在项目中应用“观察者模式”清除层层`if...else if...`的臃肿代码一、前言二、项目中遇到的问题三、用观察者模式解决3.1 观察者模式回顾3.2 ...
  • 【设计模式】Unity3D 观察者模式

    千次阅读 2022-04-19 22:26:05
    Unity3d实现观察者模式
  • 这么天里,我已经读完了《大话设计模式》。放寒假了,没有了通过学业考试的重担,阅读技术书籍的时间页出来了不少。 在今天我刚阅读了《kafka权威指南》的第一章,关于介绍kafka这个消息发布与订阅系统的概念。...
  • 比默认的 Model/Collection request事件为您做的更。 主要好处是 提供健壮的生命周期事件( before-send 、 after-send 、 success 、 error 、 complete ) 发出特定于类型的 XHR 事件以允许聚焦绑定 能够查看...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 154,682
精华内容 61,872
热门标签
关键字:

多观察的好处