设计模式-观察者模式
1、定义
定义对象的一种一对多/一的依赖关系。当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。
2、实现观察者模式
观察者接口:
package com.cn.shejimoshi.guanchazhemoshi; public interface Observer { void update(WeatherSubject weatherSubject); }
观察者实现类:
package com.cn.shejimoshi.guanchazhemoshi; public class ConcreteObserver implements Observer { private String observerName;//观察者名称 public ConcreteObserver(String observerName) { this.observerName = observerName; } /** * 各个观察者被通知,是通过在主题中调用该观察者的方法实现的 * @param weatherSubject */ public void update(WeatherSubject weatherSubject) { System.out.println("观察者 "+observerName+" 获取的信息是:" +((ConcreteWeatherSubject)weatherSubject).getWeatherContext()); } }
主题:
package com.cn.shejimoshi.guanchazhemoshi; import java.util.ArrayList; import java.util.List; public class WeatherSubject { List<Observer> observers=new ArrayList<Observer>();//维护观察者的集合 public void addObserver(Observer observer){//添加观察者到集合 observers.add(observer); } public void notifyObservers(){//通知各个观察者--实际使用观察者对象调用观察者的update方法 for (Observer observer: observers){ observer.update(this); } } public List<Observer> getObservers() { return observers; } public void setObservers(List<Observer> observers) { this.observers = observers; } }
主题子类:
package com.cn.shejimoshi.guanchazhemoshi; public class ConcreteWeatherSubject extends WeatherSubject { private String weatherContext; public void setWeatherContext(String weatherContext) {//主题中的weatherContext发生变化,则去通知观察者 this.weatherContext = weatherContext; this.notifyObservers(); } public String getWeatherContext() { return weatherContext; } }
测试方法:
public class WeatherSubjectTest { @Test public void test(){ //创建观察者 Observer observer1=new ConcreteObserver("小明"); Observer observer2=new ConcreteObserver("小花"); //创建主题 ConcreteWeatherSubject weatherSubject=new ConcreteWeatherSubject();
//在主题中注册观察者 weatherSubject.addObserver(observer1); weatherSubject.addObserver(observer2); //主题变化内容 weatherSubject.setWeatherContext("下雨天留客天天留我不留"); } }
说明:上述实例中,观察者的update方法参数为主题的实例,该方式是观察者通过获取主题的实例,从而获取主题信息实现,称为拉模型(观察者通过获取主题的引用去拉取数据);如果update方法的参数为具体的数据,如改成String类型的数据,然后在主题中调用时传入weatherContext,称为推模型(主题具体信息推给观察者)
3、使用java提供的观察者实现
- 不需要定义主题父类、不需要定义观察者接口,JDK已提供
- 主题不需要维护观察者,JDK已提供
- 主题在通知必须调用setChanged(),否则通知不起作用
观察者实现:
package com.cn.shejimoshi.guanchazhemoshi2; import java.util.Observable; import java.util.Observer; public class ConcreteObserver implements Observer { private String observerName; ConcreteObserver(String observerName){ this.observerName=observerName; } //既可以支持推模式,也可以支付拉模式。如果主题使用推模式,则同时支持;如果主题使用拉模式,则只支持拉模式(即arg为空) public void update(Observable o, Object arg) { System.out.println("观察者"+observerName+" 具体类信息:"+o); System.out.println("观察者"+observerName+" 参数信息:"+arg); } }
主题子类:
package com.cn.shejimoshi.guanchazhemoshi2; import java.util.Observable; public class ConcreteSubject extends Observable { private String context; private int number; public String getContext() { return context; } public void setNumber(int number) { this.number = number; } public void setContext(String context) { this.context = context; this.setChanged(); this.notifyObservers(context); //推模式 // this.notifyObservers();//拉模式 } @Override public String toString() { return "ConcreteSubject{" + "context='" + context + '\'' + ", number=" + number + '}'; } }
测试方法:
public class ConcreteSubjectTest { @Test public void test(){ ConcreteObserver concreteObserver1=new ConcreteObserver("大哈"); ConcreteObserver concreteObserver2=new ConcreteObserver("大牛"); ConcreteSubject concreteSubject=new ConcreteSubject(); concreteSubject.addObserver(concreteObserver1); concreteSubject.addObserver(concreteObserver2); concreteSubject.setNumber(121); concreteSubject.setContext("我在世界之巅"); } }