JAVA设计模式之观察者模式

来源:互联网 发布:U递索引超出了数组界限 编辑:程序博客网 时间:2024/05/22 05:24

观察者模式

1.什么是观察者模式?

Head Frist设计模式》中定义是:在对象之间定义一对多的依赖关系,当一个对象改变状态,依赖它的对象都会收到通知并自动更新。

观察者和被观察者之间用松耦合的方式结合,被观察者不知道观察者的细节,只知道观察者实现了统一的观察者接口Observer,在JDK中已经默认提供。

被观察者可以称为主题(Subject),默认都继承了统一的被观察者超类Observable

有多个观察者时,通知是无序的,不能依赖特定的通知顺序,否则会有意外的错误。

观察者模式传递数据有两种:推模式拉模式

 

2.角色

观察者模式的角色组成(图片来源于网络):


(1)、被观察者接口Subject):抽象被观察者的基本行为,所有被观察者必须要实现此类(注意:JDK自带实现是一个超类Observable,不是一个接口)。

(2)、观察者接口Observer):抽象观察者的基本行为,是所有观察者的共同接口(注意:JDK自带实现接口也是Observer类,只有一个方法:update(Observable o, Object arg))。

(3)、具体被观察者ConcreteSubject):具体的一个被观察者,可以拥有多个观察者(如果使用JDK自带的,就只需要继承Observable类)。

(4)、具体观察者ConcreteObserver):具体的观察者,必须实现Observer接口(Observer接口可以自己实现,也可以使用JDK自带的)。

 

3.优点

1)、在观察者与被观察者之间建立了抽象的耦合,使两者之间采用松耦合的方式结合。被观察者不知道观察者的具体细节,只知道一个注册观察者的列表和观察者们统一实现的共同接口。

2)、观察者模式支持广播通讯。被观察者会向所有注册过的观察者发送通知。

4.缺点

1)、观察者数目太多的时候,被观察者通知观察者的时间会很长。

2)、如果观察者和被观察者之间存在循环依赖,可能导致系统奔溃。

3)、观察者知道被观察者发生了变化,但是不知道发生了怎样的变化。

4)、java.util.Observable的缺点:首先Observable是一个类,不是一个接口,在面向接口编程的原则中不是一件好事儿,继承Observable类就不能继承其他类(java单一继承),因此限制了Observable类的复用潜力。

5.使用场景

1)、当一个对象状态的变化需要通知另外几个对象时。

2)、发布-订阅模式、广播模式的功能。

6.实例代码

1)首先自己实现一个观察者模式。

此功能为“天气播报功能”。WeatherDate是一个被观察者,主要负责获取天气数据。数据获取过后分别通知ShiduDisplay(湿度显示板)、WenduDisplay(温度显示板)和YaliDisplay(压力显示板)三个显示板进行显示。

(a)、被观察者接口

/**

 * 被观察者接口

* @date 2017年8月24日 下午4:33:26

 */

public interface Subject {

/*

 * 添加观察者

 */

public void addObserver(Observerobserver);

/*

 * 移除观察者

 */

public void removeObserver(Observerobserver);

/*

 * 通知观察者

 */

public void notifyObserver();

}

 

(b)被观察者-天气数据获取类

/**

 * 天气信息获取类

* @date 2017年8月29日 上午11:32:49

 */

public class WeatherDateimplements Subject{

 

private Float wendu;//温度

private Float shidu;//湿度

private Float yali;//压力

/*

 * 已注册的观察者集合

 */

private List<Observer>observerList;

public Float getWendu() {

return this.wendu;

}

 

public Float getShidu() {

return this.shidu;

}

 

public Float getYali() {

return this.yali;

}

public WeatherDate(){

observerList=new ArrayList<Observer>();

}

@Override

public void addObserver(Observerobserver) {

if(observerList!=null)

observerList.add(observer);

}

 

@Override

public void removeObserver(Observerobserver) {

if(observer!=null){

int index=observerList.indexOf(observer);

if(index>=0)

observerList.remove(index);

}

}

 

@Override

public void notifyObserver() {

if(observerList!=null &&observerList.size()>0){

for(Observer ob:observerList)

ob.update(this,null);

}

}

 

/**

 * 设置天气数据

* @param wendu

* @param shidu

* @param yali

* @return void 返回类型

* @throws 

* @auter leitao

* @date 2017年8月29日

 */

public void setWeatherData(Floatwendu,Floatshidu,Float yali){

this.wendu=wendu;

this.shidu=shidu;

this.yali=yali;

notifyObserver();

}

}

 

(c)观察者接口

/**

 * 观察者接口

* @date 2017年8月24日 下午4:35:08

 */

public interface Observer {

/**

 * 观察者动作

* @param subject 被观察者对象

* @param arg     参数

* @auter leitao

* @date 2017年8月29日

 */

public void update(Subjectsubject,Objectarg);

}

 

(d)湿度显示板

public class ShiduDisplayimplements Observer, Display {

 

private Float shidu;// 湿度

 

public ShiduDisplay(SubjectweatherDate) {

// 注册成为观察者

if (weatherDate !=null)

weatherDate.addObserver(this);

}

 

@Override

public void display() {

System.out.println(" shiduDisplay getData:" +this.shidu);

 

}

 

@Override

public void update(Subjectsubject, Objectarg) {

if (subject instanceof WeatherDate) {

WeatherDate wd = (WeatherDate) subject;

this.shidu =wd.getShidu();

display();

}

}

}

 

(e)温度显示板

public class WenduDisplayimplements Observer, Display {

 

private Float wendu;// 温度

 

public WenduDisplay(SubjectweatherDate) {

// 注册成为观察者

if (weatherDate !=null)

weatherDate.addObserver(this);

}

 

@Override

public void display() {

System.out.println(" wenduDisplay getData:" +this.wendu);

 

}

 

@Override

public void update(Subjectsubject, Objectarg) {

if (subject instanceof WeatherDate) {

WeatherDate wd = (WeatherDate) subject;

this.wendu =wd.getWendu();

}

display();

}

}

 

(f)压力显示板

public class YaliDisplayimplements Observer, Display {

 

private Float yali;// 压力

 

public YaliDisplay(SubjectweatherDate) {

// 注册成为观察者

if (weatherDate !=null)

weatherDate.addObserver(this);

}

 

@Override

public void display() {

System.out.println(" yaliDisplay getData:" +this.yali);

 

}

 

@Override

public void update(Subjectsubject, Objectarg) {

if (subject instanceof WeatherDate) {

WeatherDate wd = (WeatherDate) subject;

this.yali =wd.getYali();

display();

}

}

}

 

(g)显示面板统一接口

/**

 *显示面板操作统一接口

* @ClassName: Display

* @author Administrator

* @date 2017年8月24日 下午4:35:55

 */

public interface Display {

public void display();

}

(h)运行

public static void main(String[]args) {

WeatherDate wd=new WeatherDate();

WenduDisplay wdd=new WenduDisplay(wd);

ShiduDisplay sdd=new ShiduDisplay(wd);

YaliDisplay yld=new YaliDisplay(wd);

//wd.removeObserver(yld);

wd.setWeatherData(27.6f, 33f, 105f);

}

 

(i)运行结果

 wenduDisplay getData:27.6

 shiduDisplay getData:33.0

 yaliDisplay getData:105.0

 

(2)通过JDK自带实现的观察者模式改造“天气播报功能”。

 

(a)天气获取类

 

import java.util.Observable;

 

import ...

 

public class WeatherDateextends Observable{

 

private Float wendu;

private Float shidu;

private Float yali;

public Float getWendu() {

return wendu;

}

public Float getShidu() {

return shidu;

}

public Float getYali() {

return yali;

}

/**

 * 更改数据

* @param wendu

* @param shidu

* @param yali

* @auter leitao

* @date 2017年8月28日

 */

public void setWeatherData(Floatwendu,Floatshidu,Float yali){

this.wendu=wendu;

this.shidu=shidu;

this.yali=yali;

setChanged();//参考如下“★”

notifyObservers();

}

}

 

(b)温度显示板

import java.util.Observable;

import java.util.Observer;

/**

 * 温度显示类

* @date 2017年8月28日 下午3:01:04

 */

public class WenduDisplayimplements Observer{

 

private Float wendu;

/**

 * 注册成为WeatherDate的观察者

 * @param weatherDate

 */

public WenduDisplay(WeatherDateweatherDate) {

if(weatherDate!=null)

    weatherDate.addObserver(this);

}

 

/**

 * 数据显示

* @auter leitao

* @date 2017年8月28日

 */

public void display() {

System.out.println(" wenduDisplay getData:"+this.wendu);

}

 

@Override

public void update(Observablesubject, Objectarg) {

if(subject instanceof WeatherDate){

WeatherDate wd=(WeatherDate)subject;

this.wendu=wd.getWendu();

}

display();

}

}

 

(c)湿度显示板

/**

 * 湿度显示类

* @date 2017年8月28日 下午2:58:16

 */

public class ShiduDisplayimplements Observer{

 

private Float shidu;

/**

 * 注册成为WeatherDate的观察者

 * @param weatherDate

 */

public ShiduDisplay(WeatherDateweatherDate) {

if (weatherDate !=null)

weatherDate.addObserver(this);

}

 

@Override

public void update(Observableo, Objectarg) {

if(o instanceof WeatherDate){

WeatherDate wd=(WeatherDate) o;

//此处采用向被观察者 拉 数据的方式获取数据

this.shidu=wd.getShidu();

display();

}

}

public void display() {

System.out.println(" shiduDisplay getData:"+this.shidu);

}

}

 

(d)压力显示板

/**

 * 压力显示类

* @date 2017年8月28日 下午3:02:22

 */

public class YaliDisplayimplements Observer {

 

private Float yali;

/**

 * 注册成为WeatherDate的观察者

 * @param weatherDate

 */

public YaliDisplay(WeatherDateweatherDate) {

if (weatherDate !=null)

weatherDate.addObserver(this);

}

 

public void display() {

System.out.println(" yaliDisplay getData:"+this.yali);

}

 

@Override

public void update(Observablesubject, Objectarg) {

if(subject instanceof WeatherDate){

WeatherDate wd=(WeatherDate) subject;

this.yali=wd.getYali();

display();

}

}

}

 

(e)运行

public static void main(String[]args) {

WeatherDate wd=new WeatherDate();

WenduDisplay wdd=new WenduDisplay(wd);

ShiduDisplay sdd=new ShiduDisplay(wd);

YaliDisplay yld=new YaliDisplay(wd);

wd.deleteObserver(yld);

//wd.deleteObservers();

//wd.countObservers();

//wd.hasChanged();

wd.setWeatherData(27.6f, 33f, 105f);

}

 

(f)运行结果

 shiduDisplay getData:33.0

 wenduDisplay getData:27.6

 

(3)JDKObservable类补充

Observable类主要方法如下:

addObserver(Observer o);//注册观察者

deleteObserver(Observer o);//删除观察者

notifyObservers();//通知所有观察者

notifyObservers(Object arg);//通知所有观察者,并向观察者传参数

deleteObservers();//删除所有观察者

setChanged();//设置通知状态

clearChanged();//刷新通知状态

hasChanged();//获取通知状态

countObservers();//获取当前注册的观察者数量

Observable类主要变量:

private boolean changed =false;//通知状态,初始值为false

private Vector obs;//观察者集合

 

 

此处着重讲一下changed参数和setChanged()方法。changed参数默认是false,主要使用与notifyObservers(Object arg)方法中,用于控制是否通知观察者们。

代码如下:

public void notifyObservers(Objectarg) {

        Object[] arrLocal;

 

        synchronized (this) {

           /*

 *如果通知状态为false则返回,如果为true则挨个调用观察者们的*update方法

 */

if (!changed)

                return;

            arrLocal = obs.toArray();

            clearChanged();

        }

 

        for (int i =arrLocal.length-1;i>=0;i--)

            ((Observer)arrLocal[i]).update(this,arg);

    }

 

★因此,在被观察者中调用notifyObservers(Object arg)notifyObservers()方法之前,必须要先调用setChanged()方法设置通知状态,否者notify方法无效。



【乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】


原创粉丝点击