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

      在阎宏博士的《JAVA与模式》一书中开头是这样描述观察者(Observer)模式的:观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

      

      观察者模式的结构

      一个软件系统通常要求在某个对象的状态变化时,其他的某些对象随之变化。观察者模式是满足这一要求的所有方案中最好的一种。结构图如下:

      

      涉及的角色:

      抽象主题(Subject)角色:抽象主题角色又叫做抽象被观察者(Observable)角色。定义一个可以增加和删除观察者对象的接口,每个主题把所有观察者对象的引用保存在一个集合(例如ArrayList)里面。

      具体主题(ConcreteSubject)角色:具体主题角色又叫做具体被观察者(Concrete Observable)角色。在具体主题的状态变化时,给所有登记过的观察者发出通知。

      抽象观察者(Observer)角色:为所有的具体观察者定义一个更新接口,收到主题的通知时更新具体观察者。

      具体观察者(ConcreteObserver)角色:存储与主题状态适应的状态,实现了抽象观察者角色的更新接口。

      抽象主题角色类

     1 public abstract class Subject {
     2     /**
     3      * 用来保存注册的观察者对象
     4      */
     5     private    List<Observer> list = new ArrayList<Observer>();
     6     /**
     7      * 注册观察者对象
     8      * @param observer    观察者对象
     9      */
    10     public void attach(Observer observer){
    11         
    12         list.add(observer);
    13         System.out.println("Attached an observer");
    14     }
    15     /**
    16      * 删除观察者对象
    17      * @param observer    观察者对象
    18      */
    19     public void detach(Observer observer){
    20         
    21         list.remove(observer);
    22     }
    23     /**
    24      * 通知所有注册的观察者对象
    25      */
    26     public void nodifyObservers(String newState){
    27         
    28         for(Observer observer : list){
    29             observer.update(newState);
    30         }
    31     }
    32 }

      具体主题角色类

     1 public class ConcreteSubject extends Subject{
     2     
     3     private String state;
     4     
     5     public String getState() {
     6         return state;
     7     }
     8 
     9     public void change(String newState){
    10         state = newState;
    11         System.out.println("主题状态为:" + state);
    12         //状态发生改变,通知各个观察者
    13         this.nodifyObservers(state);
    14     }
    15 }

      抽象观察者角色类

    1 public interface Observer {
    2     /**
    3      * 更新接口
    4      * @param state    更新的状态
    5      */
    6     public void update(String state);
    7 }

      具体观察者角色类

     1 public class ConcreteObserver implements Observer {
     2     //观察者的状态
     3     private String observerState;
     4     
     5     @Override
     6     public void update(String state) {
     7         /**
     8          * 更新观察者的状态,使其与目标的状态保持一致
     9          */
    10         observerState = state;
    11         System.out.println("状态为:"+observerState);
    12     }
    13 
    14 }

      客户端类

     1 public class Client {
     2 
     3     public static void main(String[] args) {
     4         //创建主题对象
     5         ConcreteSubject subject = new ConcreteSubject();
     6         //创建观察者对象
     7         Observer observer = new ConcreteObserver();
     8         //将观察者对象登记到主题对象上
     9         subject.attach(observer);
    10         //改变主题对象的状态
    11         subject.change("new state");
    12     }
    13 
    14 }

      结果:

      

      客户端先创建具体主题类的实例和一个观察者对象。然后,它调用主题对象的attach方法,将观察者对象登记到主题对象上,即把它加入到主题对象的集合中。客户端调用主题对象的change方法后改变了主题对象的状态。主题对象在状态变化时,调用父类的notifyObservers方法,通知所有登记过的观察者对象,更新它们的状态。

     

      推模型和拉模型

      观察者模式分为推模型和拉模型两种方式。

      推模型:不管观察者对象是否需要,主题对象都会向观察者对象推送主题的详细信息。

      拉模型: 主题对象在通知观察者对象时,只传递少量信息。如果观察者对象需要更具体的信息,观察者对象需要主动向主题对象获取,相当于观察者对象从主题对象中拉数据。通过update方法把主题对象传递给观察者对象,通过主题对象获取信息。

      前面的例子是推模型,下面给出一个拉模型的实例:

      拉模型的抽象观察者类 

    1 public interface Observer {
    2     /**
    3      * 更新接口
    4      * @param subject 传入主题对象,方面获取相应的主题对象的状态
    5      */
    6     public void update(Subject subject);
    7 }

      拉模型的具体观察者类

     1 public class ConcreteObserver implements Observer {
     2     //观察者的状态
     3     private String observerState;
     4     
     5     @Override
     6     public void update(Subject subject) {
     7         /**
     8          * 更新观察者的状态,使其与目标的状态保持一致
     9          */
    10         observerState = ((ConcreteSubject)subject).getState();
    11         System.out.println("观察者状态为:"+observerState);
    12     }
    13 
    14 }

      拉模型的抽象主题类

     1 public abstract class Subject {
     2     /**
     3      * 用来保存注册的观察者对象
     4      */
     5     private    List<Observer> list = new ArrayList<Observer>();
     6     /**
     7      * 注册观察者对象
     8      * @param observer    观察者对象
     9      */
    10     public void attach(Observer observer){
    11         
    12         list.add(observer);
    13         System.out.println("Attached an observer");
    14     }
    15     /**
    16      * 删除观察者对象
    17      * @param observer    观察者对象
    18      */
    19     public void detach(Observer observer){
    20         
    21         list.remove(observer);
    22     }
    23     /**
    24      * 通知所有注册的观察者对象
    25      */
    26     public void nodifyObservers(){
    27         
    28         for(Observer observer : list){
    29             observer.update(this);
    30         }
    31     }
    32 }

      拉模型的具体主题类

     1 public class ConcreteSubject extends Subject{
     2     
     3     private String state;
     4     
     5     public String getState() {
     6         return state;
     7     }
     8 
     9     public void change(String newState){
    10         state = newState;
    11         System.out.println("主题状态为:" + state);
    12         //状态发生改变,通知各个观察者
    13         this.nodifyObservers();
    14     }
    15 }

      两种模式的比较

      推模型是假设主题对象知道观察者对象需要的数据;而拉模型是主题对象不知道观察者对象需要什么数据。

      推模型可能会使观察者对象难以复用,因为观察者对象的update方法是按需要定义形参,可能无法兼顾没有考虑到的使用情况。当新情况出现时,就可能需要提供新的update方法,或者重新实现观察者类;而拉模型不会出现这种情况,因为观察者对象的update方法的形参是主题对象,通过主题对象可以获取所有信息。

     

      Java对观察者模式的支持

      java.util库提供了一个Observer接口和一个Observable类。

      Observer接口

      只定义了一个update方法。当被观察者对象的状态变化时,被观察者对象的notifyObservers方法就会调用该方法。

    1 public interface Observer {
    2     void update(Observable o, Object arg);
    3 }

      Observable类

      被观察者类相当于抽象主题类,是java.util.Observable类的子类。有两个方法对Observable的子类来说很重要:一个是setChanged方法,被调用后会设置一个标记变量,代表被观察者对象的状态变化了。第二个是notifyObservers方法,被调用时会调用所有登记过的观察者对象的update方法,更新观察者对象。

     1 public class Observable {
     2     private boolean changed = false;
     3     private Vector obs;
     4    
     5     /** Construct an Observable with zero Observers. */
     6 
     7     public Observable() {
     8     obs = new Vector();
     9     }
    10 
    11     /**
    12      * 将一个观察者添加到观察者聚集上面
    13      */
    14     public synchronized void addObserver(Observer o) {
    15         if (o == null)
    16             throw new NullPointerException();
    17     if (!obs.contains(o)) {
    18         obs.addElement(o);
    19     }
    20     }
    21 
    22     /**
    23      * 将一个观察者从观察者聚集上删除
    24      */
    25     public synchronized void deleteObserver(Observer o) {
    26         obs.removeElement(o);
    27     }
    28 
    29     public void notifyObservers() {
    30     notifyObservers(null);
    31     }
    32 
    33     /**
    34      * 如果本对象有变化(那时hasChanged 方法会返回true)
    35      * 调用本方法通知所有登记的观察者,即调用它们的update()方法
    36      * 传入this和arg作为参数
    37      */
    38     public void notifyObservers(Object arg) {
    39 
    40         Object[] arrLocal;
    41 
    42     synchronized (this) {
    43 
    44         if (!changed)
    45                 return;
    46             arrLocal = obs.toArray();
    47             clearChanged();
    48         }
    49 
    50         for (int i = arrLocal.length-1; i>=0; i--)
    51             ((Observer)arrLocal[i]).update(this, arg);
    52     }
    53 
    54     /**
    55      * 将观察者聚集清空
    56      */
    57     public synchronized void deleteObservers() {
    58     obs.removeAllElements();
    59     }
    60 
    61     /**
    62      * 将“已变化”设置为true
    63      */
    64     protected synchronized void setChanged() {
    65     changed = true;
    66     }
    67 
    68     /**
    69      * 将“已变化”重置为false
    70      */
    71     protected synchronized void clearChanged() {
    72     changed = false;
    73     }
    74 
    75     /**
    76      * 检测本对象是否已变化
    77      */
    78     public synchronized boolean hasChanged() {
    79     return changed;
    80     }
    81 
    82     /**
    83      * Returns the number of observers of this <tt>Observable</tt> object.
    84      *
    85      * @return  the number of observers of this object.
    86      */
    87     public synchronized int countObservers() {
    88     return obs.size();
    89     }
    90 }

      如何使用?

      被观察者类Watched

     1 public class Watched extends Observable{
     2     
     3     private String data = "";
     4     
     5     public String getData() {
     6         return data;
     7     }
     8 
     9     public void setData(String data) {
    10         
    11         if(!this.data.equals(data)){
    12             this.data = data;
    13             setChanged();
    14         }
    15         notifyObservers();
    16     }
    17     
    18     
    19 }

      观察者类Watcher

     1 public class Watcher implements Observer{
     2     
     3     public Watcher(Observable o){
     4         o.addObserver(this);
     5     }
     6     
     7     @Override
     8     public void update(Observable o, Object arg) {
     9         
    10         System.out.println("状态发生改变:" + ((Watched)o).getData());
    11     }
    12 
    13 }

      测试类Test

    public class Test {
    
        public static void main(String[] args) {
            
            //创建被观察者对象
            Watched watched = new Watched();
            //创建观察者对象,并将被观察者对象登记
            Observer watcher = new Watcher(watched);
            //给被观察者状态赋值
            watched.setData("start");
            watched.setData("run");
            watched.setData("stop");
    
        }
    
    }

      先创建Watched和Watcher对象。在创建Watcher对象时,将Watched对象作为参数传入。然后Watched对象调用setData方法,更改Watched对象的状态。Watched对象通知登记过的Watcher对象,调用它的update方法。

      参考资料

      《JAVA与模式》之观察者模式

  • 相关阅读:
    Enterprise Solution 界面设计规范
    大型.NET商业软件代码保护技术 技术与实践相结合保护辛苦创造的劳动成果
    开源WinForms界面开发框架Management Studio 选项卡文档 插件 Office 2007蓝色风格 后台线程
    解析Visual Studio 2015促进生产力的10个新功能
    LLBL Gen Pro 4.2 Lite 免费的对象关系映射开发框架与工具
    Windows Server 2012部署Enterprise Solution 5.4
    ASP.NET MVC:some benefits of asp.net mvc
    Python:Opening Python Classes
    ASP.NET MVC:4 Ways To Prevent Duplicate Form Submission(转载)
    架构:The Onion Architecture : part 3(洋葱架构:第三篇)(转载)
  • 原文地址:https://www.cnblogs.com/WJQ2017/p/7746416.html
Copyright © 2011-2022 走看看