设计模式-观察者模式

本文详细介绍了观察者模式,包括模式的定义、使用场景、UML类图及其实现。特别探讨了推模型和拉模型的区别,推模型假定主题知道观察者需要的数据,可能导致复用性问题;而拉模型让观察者自行从主题获取所需数据,具有更好的适应性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.观察者模式介绍

    观察者模式是一个使用频率非常高的模式,它最常用的是GUI系统、订阅-发布系统。因为这个模式的重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。

2.观察者模式的定义

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

3.观察者模式的使用场景

    关联行为场景

        关联行为是可以拆分的,而不是“组合”的关系

    事件多级触发场景

    跨系统的消息交换场景

        如 消息队列、事件总线的处理机制

4.观察者模式的UML类图


5.具体实现

    抽象主题角色类

    /**
     * 抽象主题类
     * Created by Teaphy
     * 2016/4/24.
     */
    public abstract class Subject {
        // 用来保存注册的观察者对象
        private List<Observer> listsObserver = new ArrayList<>();

        /**
         * 注册观察者对象
         * @param observer    观察者对象
         */
        public void attach(Observer observer){

            listsObserver.add(observer);
            System.out.println("Attached an observer");
        }
        /**
         * 删除观察者对象
         * @param observer    观察者对象
         */
        public void detach(Observer observer){

            listsObserver.remove(observer);
        }
        /**
         * 通知所有注册的观察者对象
         */
        public void nodifyObservers(String newState){

            for(Observer observer : listsObserver){
                observer.update(newState);
            }
        }
    }

    具体主题角色类

    /**
     * 具体主题类
     * Created by Teaphy
     * 2016/4/24.
     */
    public class ConcreteSubject extends Subject {
        private String msg;


        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        public void publish(String msg) {
            nodifyObservers(msg);
        }
    }

    抽象观察者角色类

    /**
     * 抽象观察者角色类
     * Created by Teaphy
     * 2016/4/24.
     */
    public interface Observer {

        /**
         * 更新接口
         * Created By
         * on 2016/4/24
         * @param msg 更新信息
         */
        void update(String msg);
    }

    具体观察者角色

   /**
     * 具体观察者角色
     * Created by Teaphy
     * 2016/4/24.
     */
    public class ObserverA implements Observer {

        /**
         * 更新观察者信息
         */
        public void update(String msg) {
            System.out.println("ObserverA - msg = [" + msg + "]");
        }
    }
    
        /**
     * 具体观察者角色
     * Created by Teaphy
     * 2016/4/24.
     */
    public class ObserverB implements Observer {

        /**
         * 更新观察者信息
         */
        public void update(String msg) {
            System.out.println("ObserverB - msg = [" + msg + "]");
        }
    }
    
        /**
     * 具体观察者角色
     * Created by Teaphy
     * 2016/4/24.
     */
    public class ObserverC implements Observer {

        /**
         * 更新观察者信息
         */
        public void update(String msg) {
            System.out.println("ObserverC - msg = [" + msg + "]");
        }
    }

    测试

    /**
     * Created by Teaphy
     * 2016/4/24.
     */
    public class TestObserver {
        public static void main(String[] args) {
            //创建主题对象
            ConcreteSubject cs = new ConcreteSubject();
            // 添加观察者对象
            cs.attach(new ObserverA());
            cs.attach(new ObserverB());
            cs.attach(new ObserverC());
            // 发布新版本
            cs.publish("英语");
        }
    }

     输出结果

    Attached an observer
    Attached an observer
    Attached an observer
    ObserverA - msg = [英语]
    ObserverB - msg = [英语]
    ObserverC - msg = [英语]

6.推模型和拉模型

    在观察者模式中,又分为推模型和拉模型两种方式。
  ● 推模型
            主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
  ●拉模型
            主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
    根据上面的描述,发现前面的例子就是典型的推模型,下面给出一个拉模型的实例。    

    拉模型的抽象观察者类

    拉模型通常都是把主题对象当做参数传递。
  
 /**
     * 抽象观察者角色类
     * Created by Teaphy
     * 2016/4/24.
     */
    public interface ObserverPull {

        /**
         * 更新接口
         * Created By
         * on 2016/4/24
         * @param subjectPull 传入主题对象,方面获取相应的主题对象的状态
         */
        void update(SubjectPull subjectPull);
    }

    拉模型的具体观察者类

   
/**
     * 具体观察者角色
     * Created by Teaphy
     * 2016/4/24.
     */
    public class ObserverPullA implements ObserverPull {

        private String msg;

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        /**
         * 更新观察者信息
         */
        public void update(SubjectPull subjectPull) {
            setMsg(((ConcreteSubjectPull)subjectPull).getMsg());
        }
    }

    拉模型的抽象主题类

    拉模型的抽象主题类主要的改变是nodifyObservers()方法。在循环通知观察者的时候,也就是循环调用观察者的update()方法的时候,传入的参数不同了
/**
     * 抽象主题类
     * Created by Teaphy
     * 2016/4/24.
     */
    public abstract class SubjectPull {
        // 用来保存注册的观察者对象
        private List<ObserverPull> listsObserver = new ArrayList<>();

        /**
         * 注册观察者对象
         * @param observer    观察者对象
         */
        public void attach(ObserverPull observer){

            listsObserver.add(observer);
            System.out.println("Attached an observer");
        }
        /**
         * 删除观察者对象
         * @param observer    观察者对象
         */
        public void detach(ObserverPull observer){

            listsObserver.remove(observer);
        }
        /**
         * 通知所有注册的观察者对象
         */
        public void nodifyObservers(){

            for(ObserverPull observer : listsObserver){
                observer.update(this);
            }
        }
    }

    拉模型的具体主题类

  跟推模型相比,有一点变化,就是调用通知观察者的方法的时候,不需要传入参数了
  
 /**
     * 具体主题类
     * Created by Teaphy
     * 2016/4/24.
     */
    public class ConcreteSubjectPull extends SubjectPull {
        private String msg;


        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        public void publish(String msg) {
            setMsg(msg);
            //状态发生改变,通知各个观察者
            nodifyObservers();
        }
    }

    测试

    /**
     * Created by Teaphy
     * 2016/4/24.
     */
    public class TestObserverPull {
        public static void main(String[] args) {
            //创建主题对象
            ConcreteSubjectPull cs = new ConcreteSubjectPull();
            ObserverPullA observerPullA = new ObserverPullA();
            ObserverPullB observerPullB = new ObserverPullB();
            ObserverPullC observerPullC = new ObserverPullC();
            // 添加观察者对象
            cs.attach(observerPullA);
            cs.attach(observerPullB);
            cs.attach(observerPullC);
            // 发布新版本
            cs.publish("英语周报20160414期");

            System.out.println("ObserverPullA - " + observerPullA.getMsg());
            System.out.println("ObserverPullB - " + observerPullB.getMsg());
            System.out.println("---------------------------------");
            // 发布新版本
            cs.publish("英语周报20160424期");
            System.out.println("ObserverPullA - " + observerPullA.getMsg());
            System.out.println("ObserverPullC - " + observerPullC.getMsg());

        }
    }

    输出结果

    ObserverPullA - 英语周报20160414期
    ObserverPullB - 英语周报20160414期
    ---------------------------------
    ObserverPullA - 英语周报20160424期
    ObserverPullC - 英语周报20160424期

7.两种模式的比较

       1.推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。

        2.推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾 没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者是干脆重新实现观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值