zoukankan      html  css  js  c++  java
  • 观察者模式

      观察者模式是非常常用的一种设计模式。在软件系统中,当一个对象的行为依赖与另一个对象的状态时,观察者模式就相当有用。若不实用观察者模式提供的通用结构,而需要实现其类似的功能,则只能在另一个线程中不停监听对象所依赖的状态。在一个复杂系统中,可能会因此开启很多线程来实现这一功能,这将使系统的性能产生额外的负担。观察者模式的意义也就在此,它可以在单线程中,使某一对象,及时得知自身所依赖的状态的变化。观察者模式的经典机构如图:

      ISubject是被观察对象,它可以增加或者删除观察者。IOberver是观察者,它依赖于ISubject的状态变化。当ISubject状态发生改变时,会通过Inform()方法通知观察者。

      观察者模式的主要角色如下表:

    角色 作用
    主题接口 指被观察的对象。当其状态发生改变或者某事件发生时,它会将这个变化通知观察者。它维护了观察者所需要依赖的状态
    具体主题 具体主题实现了主题接口中的方法。如新增观察者、删除观察者和通知观察者。其内部维护一个观察者列表
    观察者接口 观察者接口定义了观察者的基本方法。当依赖状态发生改变时,主题接口就会调用观察者的update()方法
    具体观察者 实现了观察者接口的update(),具体处理了当被观察者状态改变或者某一事件发生时的业务逻辑

      主题接口的实现如下:

    public interface ISubject {
        void attach(IObserver observer);    //添加观察者
        void detach(IObserver observer);    //删除观察者
        void inform();          //通知观察者
    }

      观察者接口的实现如下:

    public interface IObserver {
        void update(Event event);       //更新观察者
    }

      一个具体的主题实现,注意,它维护了观察者队列,提供了增加和删除观察者的方法,并通过其inform()通知观察者。

      

    public class ConcreteSubject implements ISubject {
        Vector<IObserver> observers = new Vector<IObserver>();
    
    
        @Override
        public void attach(IObserver obserser) {
            observers.add(obserser);
        }
    
        @Override
        public void detach(IObserver obserser) {
            observers.removeElement(obserser);
        }
    
        @Override
        public void inform() {
            Event event = new Event();
            for (IObserver ob : observers) {
                ob.update(event);
            }
        }
    }

      一个具体的观察者实现如下,当其监听的状态发生改变时,update()方法就会被主题回调,进而可以在观察者内部进行业务逻辑的处理。

    public class ConcreteObserver implements IObserver {
        @Override
        public void update(Event event) {
            System.out.println("observer receives information");
        }
    }

      观察者模式是如此常用,以至于JDK内部就已经为开发人员准备了一套观察者模式的实现。它位于java.util包中,包括java.util.Observable类和java.util.Observer接口,它们的关系如下图:

       在java.util.Observable类中,已经实现了主要的功能,如增加观察者、删除观察者和通知观察者,开发人员可以直接通过继承Observable使用这些功能。java.util.Observer接口是观察者接口,它的update()方法会在java.util.Observable的notifyObservers()方法中被回调,以获得最新的状态变化。通常在观察者模式中Observer接口总是应用程序的核心扩展对象,具体的业务逻辑总是被封装在update()方法中。

      在JDK中,观察者模式也得到了普遍的应用。一个最典型的应用比那时Swing框架的JButton实现,它的时间处理机制如下图所示:

       JButton继承自AbstractButton,在AbstractButton中维护类一组监听器,它们就装扮着被观察者的角色。而AbstractButton本身就是被观察者对象。监听器ActionoListener并不是依靠循环监听去获得按钮何时被单击,而是当按钮被单击时,通过AbstractButton的fireActionPermed()方法回调ActionListener.actionPerformed()方法实现。基于这种结构,在应用程序开发时,只需要简单地实现ActionListener接口(也就是Observer),并将其添加到按钮(Subject角色)的观察者列表中,那么当单机事件发生,就可以自动促发监听器的业务处理函数。下面从观察者模式的角度,分析一段按钮单击处理的代码:

    public static class BtnListener implements ActionListener {
        //这就是具体的观察者
        
        @Override
        public void actionPerformed(ActionEvent e) {
            //在fireActionPerformed()中被回调
            System.out.println("click"); //按钮单击时,由具体观察者处理业务
        }
    }
    
    public static void main(String[] args) {
        JFrame  p = new JFrame();
        JButton btn = new JButton("Click ME");  //新建具体主题
        btn.addActionListener(new BtnListener());   //在具体主题中,假如观察者
        p.add(btn);
        p.pack();
        p.setVisible(true);
    }

      当按钮被单击时,通过被观察对象通知观察者,以下时AbstractButton中的一段事件处理代码,显示了被观察对象如何通知观察者:

    protected void fireActionPerformed(ActionEvent event) {
        Object[] listeners = listtenerList.getListenerList();//这里就是应用层
                                                            //实现的ActionListener
        ActionEvent e = null;
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i] == ActionListener.class) {
                if (e == null) {
                    String actionCommand = event.getActionCommand();
                    if(actionCommand == null) {
                        actionCommand = getActionCommand():
                    }
                    e = new ActionEvent(AbstractButton.this,
                            ActionEvent.ACTION_PERFORMED,
                            actionCommand,
                            event.getWhen(),
                            event.getModifiers());  //构造事件参数
                            //告诉应用层是何种事件发生
                }
                ((ActionListener) listeners[i+1]).actionPerformed(e);
                                                        //回调应用层的实现
            }
        }
    }
  • 相关阅读:
    《C#从现象到本质》读书笔记(八)第10章反射
    《C#从现象到本质》读书笔记(七)第9章 泛型
    《C#从现象到本质》读书笔记(六)第8章委托和事件
    《C#从现象到本质》读书笔记(五)第5章字符串第6章垃圾回收第7章异常与异常处理
    求1+2+……+n的和
    回溯法的应用举例
    回溯法
    翻转单词顺序列
    左旋转字符串
    和为S的两个数字
  • 原文地址:https://www.cnblogs.com/klyjb/p/11540264.html
Copyright © 2011-2022 走看看