《Java设计模式之观察者模式》

本文深入解析了观察者模式的概念及其实现方式,包括其结构、推模型与拉模型的区别,并探讨了Java内置观察者模式的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《观察者模式》

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

  1.观察者模式的结构

        抽象主题角色(Subject):抽象主题将所有的观察者保存在一个集合中,并定义了增加、删除、通知三个方法。
        具体主题角色(ConcreteSubject):当自己的状态发生变化时,就会通知所有登记过的观察者。
        抽象观察者角色(Observer):定义了一个更新的方法,让具体主题对象在通知时会调用该方法。
        具体观察者角色(ConcreteObserver):存储和主体相同的状态,当被通知时,具体的update方法被调用,状态得以更新。

  2.自定义观察者模式实现HeadFirst设计模式中的气象站的例子:

//通知者接口(Subject)
public interface Subject {
	//添加观察者
	void addObserver(Observer obj);
	//移除观察者
	void removeObserver(Observer obj);
	//通知观察者
	void notifyObservers();
}
//主题类(ConcreteSubject)
public class WeatherData implements Subject {
	private List<Observer> list;
	private float temp;
	private float humidity;
	private float pressure;
	//初始化集合
	public WeatherData() {
		list = new ArrayList<Observer>();
	}
	//添加观察者
	@Override
	public void addObserver(Observer obj) {
		list.add(obj);
	}
	//移除观察者
	@Override
	public void removeObserver(Observer obj) {
		list.remove(list.indexOf(obj));
	}
	//通知所有观察者,会调用所有观察者的update()
	@Override
	public void notifyObservers() {
		for (Observer obj : list) {
			obj.update(temp, humidity, pressure);
		}
	}
	//一旦气象站监测的数据发生变化,就会调用该方法
	public void mesureChanged(){
		notifyObservers();
	}
	//该方法用来模拟监测数据变化,会调用mesureChanged方法
	public void setMeasurements(float temp,float humidity,float pressure){
		this.temp = temp;
		this.humidity = humidity;
		this.pressure = pressure;
		mesureChanged();
	}
}
//抽象观察者(Observer)
public interface Observer {
	void update(float temp,float humidity,float pressure);
}
//展示的公告(ConcreteObserver)
public class CurrentConditionsDisplay implements Observer{
	private float temp;
	private float humidity;
	private float pressure;
	//直接在构造函数中完成观察者的添加
	public CurrentConditionsDisplay(Subject subject) {
		subject.addObserver(this);
	}
	//展示数据
	public void display() {
		System.out.println("温度为"+temp);
		System.out.println("湿度为"+humidity);
		System.out.println("压力为"+pressure);
	}
	@Override
	public void update(float temp, float humidity, float pressure) {
		this.temp = temp;
		this.humidity = humidity;
		this.pressure = pressure;
		display();
	}
}
//测试
public class TestObserver {
	@Test
	public void testObserver(){
		//创建通知者
		WeatherData wd = new WeatherData();
		//创建观察者
		CurrentConditionsDisplay ccd = new CurrentConditionsDisplay(wd);
		//模拟测试温度变化
		wd.setMeasurements(20, 1l, 22);
	}
}

  3.观察者模式中的推模型和拉模型

        推模型:就像上面的示例,如果气象站监测的数据发生变化,它会把所有的数据全部发送给观察者, 不管该观察者是否需要所有的数据。
        拉模型:主题在通知观察者时,一般会将自身对象通过update()方法传递过去,然后自身提供数据的getter方法,让观察者通过自身需要通过getter()方法获取想要的数据。

  通过Java.util.Observable类和Java.util.Observer接口可以Java内置的观察者模式,我们通过Java内置的观察者模式将上面的示例改造成拉模型:
//继承Jdk内置通知者
public class WeatherData extends Observable {
	private float temp;
	private float humidity;
	private float pressure;
	
	//一旦气象站监测的数据发生变化,就会调用该方法
	public void mesureChanged(){
		//调用通知方法前,必须调用setChanged()
		setChanged();
		notifyObservers();
	}
	//该方法用来模拟监测数据变化,会调用mesureChanged方法
	public void setMeasurements(float temp,float humidity,float pressure){
		this.temp = temp;
		this.humidity = humidity;
		this.pressure = pressure;
		mesureChanged();
	}
	//听过getter方法供观察者获取数据
	public float getTemp() {
		return temp;
	}
	public float getHumidity() {
		return humidity;
	}
	public float getPressure() {
		return pressure;
	}
}
//实现Java内置的Observer接口
public class CurrentConditionsDisplay implements Observer {
	private Observable obs;
	private float temp;
	private float humidity;
	private float pressure;
	//传入通知者,用于添加观察者
	public CurrentConditionsDisplay(Observable obs) {
		obs.addObserver(this);
		this.obs = obs;
	}
	@Override
	public void update(Observable obs, Object obj) {
		if (obs instanceof WeatherData){
			//通过getter方法来获取数据
			WeatherData wd = (WeatherData) obs;
			this.temp = wd.getTemp();
			this.humidity = wd.getHumidity();
			this.pressure = wd.getPressure();
		}
		display();
	}
	
	public void display(){
		System.out.println("温度为"+temp);
		System.out.println("湿度为"+humidity);
		System.out.println("压力为"+pressure);
	}
}
//测试
public class TestObserver {
	@Test
	public void testObserver(){
		//创建主题
		WeatherData wd = new WeatherData();
		//创建观察者
		CurrentConditionsDisplay ccd = new CurrentConditionsDisplay(wd);
		//更新主题数据
		wd.setMeasurements(10, 12, 13);
	}
}
  尽管Java为我们提供了内置的观察者模式,但是其中也有不足之处。我们发现 Observable是一个类而不是接口,所以我们要实现观察者模式就必须继承它,而由于Java并不支持多继承,所以我们的主题类就没办法继承别的类。而且由于Observable的setChanged方法被protected修饰,导致我们没法通过组合的方式调用该方法,这也违法了Java的设计原则“多组合,少继承"。

  4.推模型和拉模型的区别  

           1)推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。

   2)推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者是干脆重新实现观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。


  5.总结

     优点:
       1.Java中有很多地方都是用了观察者模式,比如Swing、RMI等,观察者模式在被观察者和观察者之间建立了一种抽象的耦合,即被观察者并不知道观察者具体的实现,只知道它们都实现了同一个就,这样可以很好的实现程序见的松耦合。
     缺点:
       1.如果使用Java内置的观察者模式,应该注意它存在的一些问题,比如在有多个观察者时,不要依赖特定的通知顺序,Observable是个类且没有实现任何借口,导致扩展性不好。
       2.如果被观察者有过多的直接观察者或间接观察者,这就会导致每一次通知的耗时过长。
 
           

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值