• 观察者模式


    前言

    最近在看《深入浅出Nodejs》,看完了第三章异步IO,觉得观察者模式在Node中十分重要于是找了几篇博客学习了一下观察者模式。

    主要的参考博客:
    《JAVA与模式》之观察者模式——java_my_life
    java:从消息机制谈到观察者模式——luoweifu

    观察者模式的定义

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

    观察者模式结构

    偷懒直接拿了别人的图:

    • Subject 抽象主题,也被叫做被观察者(Observable),抽象主题提供一个接口,可以添加和删除观察者
    • ConcreteSubject 具体主题,也被叫做具体被观察者(Concreate Obervable),存放有关状态,并且在状态改变时通知观察者
    • Observer 抽象观察者,为所有具体观察者定义一个更新接口,用于在得到主题更新时更新自己,并做一些相应操作。
    • ConcreteObserver 具体观察者,存储与主题相关的状态并且实现更新接口,可以持有一个具体主题对象的引用。

    推模型和拉模型

    观察者模式可以分为拉模型和推模型两种:

    • 推模型 主题对象像观察者推送信息,不管观察者是否需要,推送的信息一般是主题对象的全部或部分数据。
    • 拉模型 主题对象在通知观察者时,只提供少量信息,若观察者需要更多信息,则直接到主题对象中获取数据。这样就需要通过update()方法将主题对象传递给观察者,或者让观察者持有一个具体主题对象的引用。

    推模型

    //Observer.java
    public interface Observer {
    	void update(String newState);
    }
    
    //Observable.java
    public abstract class Observable {
    	private ArrayList<Observer> observerList = new ArrayList<>();
    	
    	public void addObserver(Observer o) {
    		observerList.add(o);
    	}
    	
    	public void removeObserver(Observer o) {
    		observerList.remove(o);
    	}
    	
    	public void nodifyObservers(String newState) {
    		for(Observer observer : observerList) {
    			observer.update(newState);
    		}
    	}
    }
    
    //ConcreteObserver.java
    public class ConcreteObserver implements Observer {
    
    	public ConcreteObserver(Observable o) {
    		o.addObserver(this);
    	}
    	
    	@Override
    	public void update(String newState) {
    		System.out.println("状态已更新:" + newState);
    	}
    }
    
    //ConcreteObservable.java
    public class ConcreteObservable extends Observable {
    	private String state = "";
    
    	public String getState() {
    		return state;
    	}
    
    	public void setState(String state) {
    		if(!this.state.equals(state)) {
    			this.state = state;
    			this.nodifyObservers(state);
    		}
    	}
    }
    
    //Client.java
    public class Client {
    	public static void main(String[] args) {
    		ConcreteObservable concreteObservable = new ConcreteObservable();
    		ConcreteObserver concreteObserver = new ConcreteObserver(concreteObservable);
    		concreteObservable.setState("神清气爽");
    	}
    }
    
    /*OUTPUT:
     状态已更新:神清气爽
     */
    

    拉模型

    Java提供的观察者模式应该就是一种拉模型(个人理解),下面的代码删去了一些源码中的注释。

    Java源码

    //Observer.java
    package java.util;
    public interface Observer {
        void update(Observable o, Object arg);
    }
    
    //Observable.java
    package java.util;
    public class Observable {
        private boolean changed = false;
        private Vector<Observer> obs;
        public Observable() {
            obs = new Vector<>();
        }
        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);
        }
        
        public void notifyObservers() {
            notifyObservers(null);
        }
        
        public void notifyObservers(Object arg) {
            Object[] arrLocal;
    
            synchronized (this) {
                /* We don't want the Observer doing callbacks into
                 * arbitrary code while holding its own Monitor.
                 * The code where we extract each Observable from
                 * the Vector and store the state of the Observer
                 * needs synchronization, but notifying observers
                 * does not (should not).  The worst result of any
                 * potential race-condition here is that:
                 * 1) a newly-added Observer will miss a
                 *   notification in progress
                 * 2) a recently unregistered Observer will be
                 *   wrongly notified when it doesn't care
                 */
                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;
        }
        
        public synchronized int countObservers() {
            return obs.size();
        }
    }
    

    需要注意的是notifyObservers()这个方法中,从Vector中取出Observers并放在一个Object数组中,这个操作是在synchronized块中的。但是调用Observer的update()没有在synchronized块中(猜想这个地方是为了防止Observer的update()也需要锁住Observable而造成死锁,也许也有防止被Observa被锁住太长时间的目的),因此会有竞争的风险

    使用

    Watched.java

    public class Watched extends Observable {
    	private String data = "";
    
    	public String getData() {
    		return data;
    	}
    
    	public void setData(String data) {
    		if(!this.data.equals(data)){
    			this.data = data;
    			setChanged();
    		}
    		this.notifyObservers();
    	}
    }
    

    Watcher.java

    public class Watcher implements Observer{
    	
    	public Watcher(Observable observable) {
    		observable.addObserver(this);
    	}
    
    	@Override
    	public void update(Observable o, Object arg) {
    		System.out.println("状态已改变:" + ((Watched)o).getData());
    	}
    }
    

    DemoClient.java

    public class DemoClient {
    	public static void main(String[] args) {
    		Watched watched = new Watched();
    		Watcher watcher = new Watcher(watched);
    		watched.setData("好——像有点不对劲!");
    	}
    }
    /*OUTPUT:
     状态已改变:好——像有点不对劲!
     */
    
  • 相关阅读:
    Skim设置豆沙绿背景色的方法
    被咬掉一口的苹果标识的快捷键
    删除 Mac OS X 中“打开方式”里重复或无用的程序列表
    Android开发学习笔记1
    新学到的Eclipse快捷键 2个
    Android开发学习笔记2
    Mac下Eclipse的自动补全设置
    Nsight Eclipse关于CUDA程序语法高亮颜色的调整
    Tecpolt for mac
    转载:Nsight颜色设置
  • 原文地址:https://www.cnblogs.com/FJH1994/p/5865040.html
Copyright © 2020-2023  润新知