观察者模式(Observer Pattern)_Java

Observer Pattern



问题:

 有一个气象站(WeatherData),用来给各种手机(苹果,三星,摩托罗拉等等)提供气象信息,要求是,当气象信息更新的时候,给各个手机发送新的天气信息。

 方案一

//Iphone类
package com.pattern.observer;
/**
 * 苹果手机
 */
public class Iphone {
	/**
	 * 苹果手机用来显示天气的方法
	 */
	public void display(float low,float height,String weather){
		System.out.println("Iphone:最低温度-"+low+",最高温度-"+height+",天气"+weather+"。");
	}
}
//Android类
package com.pattern.observer;
/**
 * 安卓手机
 */
public class Android {
	/**
	 * 安卓手机用来显示天气的方法
	 */
	public void display(float low,float height,String weather){
		System.out.println("Android:最低温度-"+low+",最高温度-"+height+",天气"+weather+"。");
	}
}
//WeatherData类
package com.pattern.observer;
/**
 * 用来给手机厂商提供天气信息的公共类
 */
public class WeatherData {

	private float low;//最低气温
	private float height;//最高气温
	private String weather;//天气情况
	/**
	 * 提供给气象人员用来更新天气信息的,当天气更新时,掉用changed方法
	 */
	public void setData(float low,float height,String weather){
		this.low=low;
		this.height=height;
		this.weather=weather;
		changed();
	}
	/**
	 * changed方法,用来给接入的手机发送天气信息
	 */
	private void changed() {
		//苹果接入,给苹果手机提供天气服务
		Iphone iphone=new Iphone();
		iphone.display(getLow(), getHeight(), getWeather());
		//安卓接入,给安卓手机提供天气服务
		Android android=new Android();
		android.display(getLow(), getHeight(), getWeather());
	}

	public float getLow() {
		return low;
	}
	public float getHeight() {
		return height;
	}
	public String getWeather() {
		return weather;
	}
	
}
//Test类
package com.pattern.observer;

public class Test {
	public static void main(String[] args) {
		WeatherData wd=new WeatherData();
		//天气人员更新天气,手机显示更新的天气
		wd.setData(32, 41, "晴天");
	}
}

方案一的问题

如果不断有手机厂商需要接入我们的天气服务,或者不断有手机厂商不需要我们的天气服务了,我们就得不停的改动WeatherData类,这是很麻烦的事情。

问题的解决
拿出版社来举例说明

  • 出版社的任务就是出版报纸
  • 你订阅了报纸,出版社就给你送
  • 你不需要该报纸了,你可以退订
  • 气象站一直提供气象服务
  • 气象站提供一个定制服务
  • 你定制了,就给你送,你取消定制了,就不给你送了

 

观察者模式

  • 定义了对象间 一对多 的依赖
  • 报社→一
  • 订阅者→多
  • 订阅者是观察者的对象

 

方案二

//Subject接口
package com.pattern.observer1;
/**
 *提供订阅服务的接口
 */
public interface Subject {
	public void registerObserver(Observer observer);//注册观察者
	public void removeObserver(Observer observer);//删除观察者
	public void notifyObserver();//通知观察者
}
//Observer接口
package com.pattern.observer1;
/**
 * 观察者接口,实现此接口后方可去订阅主题,天气主题的观察者需要提供显示天气的方法
 */
public interface Observer {
	//观察者必须实现此方法,用来显示天气
	public void display(float low,float height,String weather);
}
//WeatherData类
package com.pattern.observer1;

import java.util.ArrayList;
import java.util.List;
/**
 * 天气数据提供订阅,实现主题接口
 */
public class WeatherData implements Subject{

	private float low;
	private float height;
	private String weather;
	private List<Observer> args=new ArrayList<Observer>();//当前气象站具有的观察者集合
	
	public float getLow() {
		return low;
	}

	public float getHeight() {
		return height;
	}

	public String getWeather() {
		return weather;
	}

	public void setData(float low,float height,String weather){
		this.low=low;
		this.height=height;
		this.weather=weather;
		changed();
	}
	private void changed() {
		notifyObserver();
	}
	/**
	 * 注册观察者,如果集合中不存在,则添加进集合
	 */
	@Override
	public void registerObserver(Observer observer) {
		if(!args.contains(observer)){
			args.add(observer);
		}
	}
	/**
	 * 删除观察者,若存在此观察者,删除此观察者
	 */
	@Override
	public void removeObserver(Observer observer) {
		if(args.contains(observer)){
			args.remove(observer);
		}
	}
	/**
	 * 数据改变时,调用此方法,给每一个观察者发送改变后的信息
	 */
	@Override
	public void notifyObserver() {
		for (Observer o : args) {
			o.display(getLow(), getHeight(), getWeather());
		}
	}

}
//Iphone类
package com.pattern.observer1;
/**
 * Iphone实现观察者接口,是一名观察者,是否订阅天气,看采用哪一种构造方法
 */
public class Iphone implements Observer{

	public Iphone(){
	}
	/**
	 * 注册天气信息,在气象站注册信息,this指当前对象
	 */
	public Iphone(Subject sub){
		sub.registerObserver(this);
	}
	/**
	 * 当成为气象站的观察者后,天气发生改变后,会收到通知,调用此方法
	 */
	@Override
	public void display(float low, float height, String weather) {
		System.out.println("Iphone:最低温度-"+low+",最高温度-"+height+",天气"+weather+"。");
	}

}
//Test类
package com.pattern.observer1;

public class Test {

	@SuppressWarnings("unused")
	public static void main(String[] args) {
		WeatherData wd=new WeatherData();
		Iphone i=new Iphone(wd);//i手机注册天气服务
		Iphone i2=new Iphone(wd);//i2手机也注册天气服务
		
		wd.removeObserver(i);//气象站取消了i手机的订阅
		wd.setData(23, 25, "windy");//这里只给i2提供天气服务
	}
}

方案三

 Java内置的观察者模式(Observable-Observer)


 

//Observable类
package com.pattern.observer2;

import java.util.Vector;

@SuppressWarnings("unchecked")
public class Observable {
    private boolean changed = false;//观察者所观察的信息是否改变
	@SuppressWarnings("rawtypes")
	private Vector obs;//用来存放观察者的集合
   
    /**
     * 构造一个没有观察者的Observable对象
     */
    @SuppressWarnings("rawtypes")
	public Observable() {
    	obs = new Vector();
    }

    /**
     * 线程安全模式下,添加一个观察者
     * 如果观察者为null,则抛异常
     * 如果当前观察者列表中没有此观察者,则添加进来
     */
	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);
    }

    /**
     * 这个方法是notifyObservers方法的重载,用来向所有观察者提供信息的
     */
    public void notifyObservers() {
    	notifyObservers(null);//调用下面的方法,传入参数null
    }

    /**
     * 如果Observable的状态发生了改变,则通知所有观察者
     */
    public void notifyObservers(Object arg) {
	
        Object[] arrLocal;//临时数组

        synchronized (this) {
		    if (!changed){
	            return;//如果没有改变,则什么也不做
		    }
	        arrLocal = obs.toArray();//若改变了将obs集合转成数组
	        clearChanged();//设置changed为false
        }
        //给每一个观察者送出通知
        for (int i = arrLocal.length-1; i>=0; i--){
            ((Observer)arrLocal[i]).update(this, arg);
        }
    }

    /**
     * 删除所有的观察者
     */
    public synchronized void deleteObservers() {
    	obs.removeAllElements();
    }

    /**
     * 用来设置Observable对象发生了改变
     */
    protected synchronized void setChanged() {
    	changed = true;
    }

    /**
     * 所有的观察者都接收到通知后,再把Observable对象设置为未改变
     */
    protected synchronized void clearChanged() {
    	changed = false;
    }

    /**
     * 判断Observable对象是否改变,返回changed的值
     */
    public synchronized boolean hasChanged() {
    	return changed;
    }

    /**
     * 统计当前Observale对象中有多少观察者
     */
    public synchronized int countObservers() {
    	return obs.size();
    }
}
//Observer接口
package com.pattern.observer2;
/**
 * 所有的观察者都要实现观察者接口
 */
public interface Observer {
    /**
     * 所有的观察者都要实现update方法,用来接收被观察对象传来的信息
     */
    void update(Observable o, Object arg);
}
//WeatherData类
package com.pattern.observer2;

import java.util.Observable;

public class WeatherData extends Observable{
	private float low;
	private float height;
	private String weather;
	
	public float getLow() {
		return low;
	}
	public float getHeight() {
		return height;
	}
	public String getWeather() {
		return weather;
	}
	/**
	 * 更新天气,调用changed方法,在changed方法中调用setChanged,notifyObservers
	 */
	public void setData(float low,float height,String weather){
		this.low=low;
		this.height=height;
		this.weather=weather;
		changed();
	}
	/**
	 * 此方法可有可无,可以将内容写到setData中,无所谓的
	 */
	private void changed() {
		setChanged();
		notifyObservers();
		/**
		 * notifyObservers(Object arg)这个方法的作用是啥呢?
		 * 使用notifyObservers()是拉模式,给用户提供获取天气信息的接口,如果需要,通过接口来拿即可
		 * 带参数的方法,可以用来实现气象站主动给你送天气,你直接用即可
		 * 例:
		 * notifyObservers(new Object[]{getLow(),getHeight(),getWeather()}
		 * Iphone中的update方法中的ars包含了此数组
		 * 直接取即可
		 * args[0]
		 * args[1]
		 * args[2]
		 * 
		 */
	}
	
}
//Iphone类
package com.pattern.observer2;

import java.util.Observable;
import java.util.Observer;

public class Iphone implements Observer{

	public Iphone(){
		
	}
	/**
	 * 用于注册到一个Observable的观察者
	 * 如果一个Iphone对象想接收某一个天气站的天气信息,就必须先注册人家的服务
	 */
	public Iphone(Observable sub){
		sub.addObserver(this);
	}
	/**
	 * 重写update方法
	 * 如果当前对象注册了,并且信息发生了改变,就给当前对象送来Observable对象o,o中包含有所有的天气信息
	 * 从o中提起信息即可
	 */
	@Override
	public void update(Observable o, Object arg) {
		WeatherData wd=(WeatherData) o;
		display(wd.getLow(),wd.getHeight(),wd.getWeather());
	}
	private void display(float low, float height, String weather) {
		System.out.println("Iphone:最低温度-"+low+",最高温度-"+height+",天气"+weather+"。");
	}

}
//Test类
package com.pattern.observer2;

public class Test {

	public static void main(String[] args) {
		WeatherData wd=new WeatherData();
		Iphone i=new Iphone(wd);
		wd.setData(12, 28, "sunny");
	}
}

Java内置观察者的缺点

Observable是一个类,类只能继承一个父类,如果其有自己的父类,则无法继承观察者模式了

关于观察者模式
  • 该模式定义了对象之间一对多的关联
  • 主题用一个共同的接口来更新观察者
  • 主题和观察者之间用松耦合的方式结合,主题不知道观察者的细节,只知道观察者实现了观察者接口

猜你喜欢

转载自hunthon.iteye.com/blog/1959794