概念
- Observer 模式是行为模式之一, 他的作用是当一个对象的状态发生变化时, 能攻自动通知其他关联的对象, 自动刷新对象的状态
- Observer 模式提供给关联对象一种同步通信的手段, 使某个对象与依赖他的其他对象之间保持状态同步
结构
- Subject(被观察者): 被观察的对象. 当需要被观察者的状态发生变化时, 需要通知队列中所有的观察者对象. Subject需要维持(添加, 删除, 通知) 一个观察者对象的队列列表
- ConcreteSubject: 被观察的具体实现. 包含一些基本的属性状态及其他操作
- Observer(观察者): 接口或抽象类. 当Subject的状态发生变化时, Observer对象将通过一个callback的函数得到通知
- ConcreteObserver: 观察者的具体实现. 得到通知后将完成一些具体的业务逻辑处理
JDK中的实现案例
代码(监听person对象的变化)
package design.observer.jdk;
import java.util.Observable;
public class Person extends Observable {
private String name;
private String sex;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
// 设置对象发生了改变
this.setChanged();
System.out.println("对象是否发生了改变: " + this.hasChanged());
// 设置的时候通知其他观察者
this.notifyObservers();
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
// 设置对象发生了改变
this.setChanged();
// 设置的时候通知其他观察者
this.notifyObservers();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
// 设置对象发生了改变
this.setChanged();
// 设置的时候通知其他观察者
this.notifyObservers();
}
}
package design.observer.jdk;
import java.util.Observable;
import java.util.Observer;
public class MyObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("对象发生变化");
}
}
package design.observer.jdk;
public class MainClass {
public static void main(String[] args) {
Person person = new Person();
// 注册观察者
person.addObserver(new MyObserver());
person.addObserver(new MyObserver());
// 删除所有观察者
// person.deleteObservers();
// 打印观察者数目
System.out.println(person.countObservers());
person.setName("龙龙");
person.setAge(18);
person.setSex("man");
}
}
观察者模式的典型应用
Observer模式的典型应用
- 侦听事件驱动程序设计中的外部事件
- 侦听/监视某个对象的状态变化
- 发布者/订阅者(publisher/subscriber)模型中, 当一个外部事件(新的产品, 消息的出现等等)被触发时, 通知邮件列表中的订阅者
手动实现案例
- 宝宝睡觉, 爸爸在一旁观察, 宝宝醒来后, 爸爸给宝宝喂食
- 代码(第一版)
package design.observer;
/**
* @ClassName: Child
* @Description:小孩类
*/
public class Child implements Runnable {
// 是否醒来 默认是睡着的
private Boolean wakeup = false;
// 醒来的行为
public void wakeUp() {
wakeup = true;
}
public Boolean getWakeup() {
return wakeup;
}
public void setWakeup(Boolean wakeup) {
this.wakeup = wakeup;
}
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
// 小孩醒来
wakeUp();
}
}
package design.observer;
/**
* @ClassName: Dad
* @Description: 父亲类 需要监控孩子 孩子醒来后 需要喂食
*/
public class Dad implements Runnable {
// 需要持有孩子的引用
private Child baby;
public Dad(Child baby) {
this.baby = baby;
}
// 喂食
public void feed(Child baby) {
System.out.println("已经给宝贝喂食!");
}
// 实时看看孩子是否醒来
@Override
public void run() {
while (!baby.getWakeup()) {
// 如果小孩子没有醒来 则一直循环
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
System.out.println("宝宝睡觉中, 5秒后即将醒来 记得喂食:" + (i + 1) + "秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 代表小孩醒来 喂食
this.feed(baby);
}
}
package design.observer;
public class LookChild {
public static void main(String[] args) {
Child baby = new Child();
new Thread(baby).start();
Dad dad = new Dad(baby);
new Thread(dad).start();
}
}
- 问题: 爸爸一直在监视这宝宝的状态 其它什么事情都干不了 这是不符合逻辑的, 爸爸也可以一边看电视 一边看孩子嘛! 需要改进….
- 代码(第二版)
package design.observer;
/**
* @ClassName: Child
* @Description:小孩类
*/
public class Child implements Runnable {
private Dad father;
public Child(Dad father) {
this.father = father;
}
// 是否醒来 默认是睡着的
private Boolean wakeup = false;
// 醒来的行为 状态改变 需要告诉老爸 所以需要持有父亲的引用
public void wakeUp() {
wakeup = true;
// 醒来后通知老爸喂食
father.feed(this);
}
public Boolean getWakeup() {
return wakeup;
}
public void setWakeup(Boolean wakeup) {
this.wakeup = wakeup;
}
@Override
public void run() {
while (!this.getWakeup()) {
// 如果小孩子自己没有醒来 则一直循环
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
System.out.println("宝宝睡觉中, 5秒后即将醒来 记得喂食: 已经过了" + (i + 1) + "秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 小孩醒来
wakeUp();
}
}
}
package design.observer;
/**
* @ClassName: Dad
* @Description: 父亲类 需要监控孩子 孩子醒来后 需要喂食
*/
public class Dad /* implements Runnable */ {// 不要再监控孩子了 不用实现Runnable接口了
public Dad() {
}
// 喂食
public void feed(Child baby) {
System.out.println("已经给宝贝喂食!");
}
}
package design.observer;
public class LookChild {
public static void main(String[] args) {
Child baby = new Child(new Dad());
new Thread(baby).start();
}
}
- 但是这样孩子醒来后, 只能通知他的爸爸, 如果其它他人在 也可以处理喂食这件事, 而且如果事件包含多个状态, 需要对每个状态进行不同的处理 一个事件的改变引起多方面变化
- 代码(最终版)
package design.observer;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName: Child
* @Description:小孩类
*/
public class Child implements Runnable {
// 持有 所有监护者 的集合
private List<WakeUpListener> wakeUpListeners = new ArrayList<>();
// 将监护者加入到集合中 以便处理醒来的事件
public void addWakeUpListener(WakeUpListener wakeUpListener) {
wakeUpListeners.add(wakeUpListener);
}
// 醒来的行为 状态改变 需要告诉老爸 所以需要持有父亲的引用
public void wakeUp() {
// 此处不直接写方法行为了 我们根据事件的情况进行处理
// 获取具有处理能力的所有监听对象
// father.actionToWakeUp(new WakeUpEvent(false, this));
for (int i = 0; i < wakeUpListeners.size(); i++) {
WakeUpListener wakeUpListener = wakeUpListeners.get(i);
// true:是饭点了 false:不是饭点
wakeUpListener.actionToWakeUp(new WakeUpEvent(true, this));
}
}
@Override
public void run() {
boolean flag = true;
while (flag) {
// 如果小孩子自己没有醒来 则一直循环
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
System.out.println("宝宝睡觉中, 5秒后即将醒来 记得喂食: 已经过了" + (i + 1) + "秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 小孩醒来
wakeUp();
flag = false;
}
}
}
package design.observer;
/**
* @ClassName: Dad
* @Description: 父亲类 需要监控孩子 孩子醒来后 需要喂食
*/
public class Dad extends GrandFather implements WakeUpListener {// 不要再监控孩子了
}
package design.observer;
/**
* @ClassName: Dad
* @Description: 爷爷类
*/
public class GrandFather implements WakeUpListener {// 不要再监控孩子了 不用实现Runnable接口了
// 直接在爷爷中声明对醒来事件的处理 爸爸继承爷爷
@Override
public void actionToWakeUp(WakeUpEvent wakeUpEvent) {
// 这里我们通过对事件对象的分析作出相应的行为
if (wakeUpEvent.getIsFoodtime()) {
// 是饭点醒来 喂食
feed();
} else {
// 不是 抱着他玩
play();
}
}
// 喂食
public void feed() {
System.out.println(this.getClass().getName().substring(this.getClass().getName().lastIndexOf(".") + 1) + " : 已经给宝贝喂食!");
}
// 抱小孩子出去玩
public void play() {
System.out.println(this.getClass().getName().substring(this.getClass().getName().lastIndexOf(".") + 1) + " : 抱小孩子出去玩!");
}
}
package design.observer;
/**
* @ClassName: WakeUpEvent
* @Description: 醒来事件的类
*/
public class WakeUpEvent {
// 醒来时间是否是饭点
private Boolean isFoodtime;
// 谁产生这个事件(事件源)
private Child source;
public WakeUpEvent(Boolean isFoodtime, Child source) {
this.isFoodtime = isFoodtime;
this.source = source;
}
public Boolean getIsFoodtime() {
return isFoodtime;
}
public void setIsFoodtime(Boolean isFoodtime) {
this.isFoodtime = isFoodtime;
}
public Child getSource() {
return source;
}
public void setSource(Child source) {
this.source = source;
}
}
package design.observer;
/**
* @ClassName: WakeUpListener
* @Description: 屋里的人都能听到小孩子哭这件事情, 都有处理小孩子醒来行为的方法
*/
public interface WakeUpListener {
void actionToWakeUp(WakeUpEvent wakeUpEvent);
}
package design.observer;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* @ClassName: PropertiesManager
* @Description:读取外部配置的监听者
*/
public class PropertiesManager {
public static Map<String, String> getPropertiesInfo() {
Map<String, String> classNameMap = new HashMap<>();
Properties properties = new Properties();
InputStream inputStream = PropertiesManager.class.getClassLoader().getResourceAsStream("properties/observer.properties");
try {
properties.load(inputStream);
Iterator<String> iterator = properties.stringPropertyNames().iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
classNameMap.put(key, properties.getProperty(key));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return classNameMap;
}
}
observer.properties
observer1=design.observer.GrandFather
observer2=design.observer.Dad
package design.observer;
import java.util.Map;
import java.util.Map.Entry;
public class LookChild {
public static void main(String[] args) throws Exception {
// 创建事件源
Child child = new Child();
// 加入监听者(如果需要加入新的监听者 那么需要修改代码 所以需要改进)
// child.addWakeUpListener(new GrandFather());
// child.addWakeUpListener(new Dad());
// 采用动态加载的思想读取配置文件
Map<String, String> map = PropertiesManager.getPropertiesInfo();
for (Entry<String, String> entry : map.entrySet()) {
child.addWakeUpListener((WakeUpListener) (Class.forName(entry.getValue()).newInstance()));
}
new Thread(child).start();
}
}