目录
背景
无特殊背景, 隶属应用纪要整理!
问题
观察者模式原理是什么?
观察者模式能够解决什么场景的问题?
使用观察者模式有什么好处?
说明
Java中已经有了对观察者模式的实现. 观察者模式的调用尽量使用异步方式触发, 且注意异常处理.
内容
观察者模式原理是什么?
观察者模式通过在应用中建立一组触发链,即A对象的变化会影响B对象,B对象的变化会影响C对象......以此实现一种基础的链式触发机制来降低对象之间的耦合度以及提高代码的复用度.
观察者模式能够解决什么场景的问题?
当某一部分数据依存于另一部分数据时,当某一部分数据的变动会影响另一部分数据时,此时就可以考虑使用观察者模式。
使用观察者模式有什么好处?
观察者模式作为一种设计模式,其出发已经决定了它的意义,因此如果在不影响系统效能的情况下,使用观察者模式能够一定程度上降低系统代码的耦合度,提升系统应用数据的协调性. 因此使用观察者模式来解决类似场景的问题是不二之选.
具体实现1 (Java提供的观察者实现方式)
下面是一则随手的Demo用例:
代码结构图
三个观察者对象
观察者对象的实现是实现了java.util包下的Observer接口:
/**
* 观察者: Cat
*/
public class Cat implements Observer {
@Override
public void update(Observable o, Object arg) {
Food food = (Food) arg;
// 如果具体的notifies方法没有把参数传入, 可以通过observable来获取参数
// if (o instanceof TargetSubject) {
// Food param = ((TargetSubject) o).getParam();
// System.out.println(param);
// }
CommonCode.animalCase(food);
}
}
/**
* 观察者: Dog
*/
public class Dog implements Observer {
@Override
public void update(Observable o, Object arg) {
Food food = (Food) arg;
// 如果具体的notifies方法没有把参数传入, 可以通过observable来获取参数
// if (o instanceof TargetSubject) {
// Food param = ((TargetSubject) o).getParam();
// System.out.println(param);
// }
CommonCode.animalCase(food);
}
}
/**
* 观察者: You
*/
public class You implements Observer {
@Override
public void update(Observable o, Object arg) {
Food food = (Food) arg;
// 如果具体的notifies方法没有把参数传入, 可以通过observable来获取参数
// if (o instanceof TargetSubject) {
// Food param = ((TargetSubject) o).getParam();
// System.out.println(param);
// }
CommonCode.animalCase(food);
}
}
目标对象(被观察对象)
目标对象则是通过继承java.util包下的Observable类来具体实现业务代码. 其中有两个重要的操作setChanged()和notifyObservers(param).
当调用setChanged()方法时,会将一个boolean类型的标志位参数设置为true, 此时如果调用notifyObservers(param)方法, notifyObservers(param)方法会判断这个标志位是否为true, 如果为true则会通知具体的挂载的观察者,目标对象发生变化,你们可以去dosomething.
这里的这两个方法由于属性动作的不同,可以在具体实现的时候单独提供触发. 下面的Demo中将二者合并在了set方法中.
/**
* 目标主题(被观察对象)
*/
public class TargetSubject extends Observable {
private Food param;
public Food getParam() {
return param;
}
public void setParam(Food param) {
this.param = param;
super.setChanged();
super.notifyObservers(param);
}
}
Run
import com.example.observerdemo.api12.observer.Cat;
import com.example.observerdemo.api12.observer.Dog;
import com.example.observerdemo.api12.observer.You;
import com.example.observerdemo.api12.param.Food;
import com.example.observerdemo.api12.param.ParamEnum;
import com.example.observerdemo.api12.subject.TargetSubject;
public class Init {
public static void main(String[] args) {
// 实例化一个目标对象.
TargetSubject subject = new TargetSubject();
// 实例化一个观察者对象监视目标对象
subject.addObserver(new Cat());
// 改变了目标对象的数据.(投食了猫粮)
subject.setParam(new Food(ParamEnum.CAT.getType(), ParamEnum.CAT.getName()));
System.out.println("只打印一条数据, 因为数据发生变动时, 只有一只猫在监控.(只有一个对象在监控)");
System.out.println("------------------------");
// 二次实例化一只狗子来监控目标对象.
subject.addObserver(new Dog());
// 继续投食(目标对象数据发生了变化)
subject.setParam(new Food(ParamEnum.DOG.getType(), ParamEnum.DOG.getName()));
System.out.println("这次打印了两条数据, 因为又牵出来一条狗子也加入了监控的队伍~~~");
System.out.println("------------------------");
// 这一次, 又多了一位监视对象.
subject.addObserver(new You());
// 第三次投食(目标对象再次发生变化)
subject.setParam(new Food(ParamEnum.OTHER.getType(), ParamEnum.OTHER.getName()));
System.out.println("这次打印三条数据, 因为又有新物种假如了监视队列~~");
System.out.println("------------------------");
// 这次拿了空饲料袋子诱惑
subject.setParam(new Food("", null));
System.out.println("又打印了三条数据, 因为不管我的饲料怎么变, 只要我倒腾了饲料盒子, 监视盒子的小家伙就会受到刺激.");
}
}
业务代码
/**
* 公共实现
*/
public class CommonCode {
public static void animalCase(Food food) {
switch (food.getType()) {
case "cat":
System.out.println("小猫咪: 吃了" + food.getName() + ", 开心 ...");
break;
case "dog":
System.out.println("小奶狗: 吃了" + food.getName() + ", 呕吐 >>>>");
break;
case "other":
System.out.println("嘿嘿嘿: 吃了" + food.getName() + ", 抢救中 >>>>");
break;
default:
System.out.println("大家对食物不感兴趣了!");
break;
}
}
}
/**
* 食物
*/
public class Food {
private String type;
private String name;
public Food() { }
public Food(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public enum ParamEnum {
CAT("cat", "猫粮"),
DOG("dog", "狗粮"),
OTHER("other", "不该吃的");
private String type;
private String name;
ParamEnum(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public String getName() {
return name;
}
}
具体实现2 (手动实现方式)
代码结构图
观察者组件定义
/**
* 观察者
*/
public interface ObserverAnimal {
/**
* 响应.
*/
void response(Food food);
}
三个观察者对象
/**
* 观察者: Cat
*/
public class Cat implements Observer {
@Override
public void update(Observable o, Object arg) {
Food food = (Food) arg;
CommonCode.animalCase(food);
}
}
/**
* 观察者: Dog
*/
public class Dog implements Observer {
@Override
public void update(Observable o, Object arg) {
Food food = (Food) arg;
CommonCode.animalCase(food);
}
}
/**
* 观察者: You
*/
public class You implements Observer {
@Override
public void update(Observable o, Object arg) {
Food food = (Food) arg;
CommonCode.animalCase(food);
}
}
目标对象(被观察的对象)
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
/**
* 目标对象
*/
public class SubjectFood {
// 观察者对象的核心
public BlockingQueue<ObserverAnimal> list;
// 初始化对象集
public SubjectFood() {
list = new LinkedBlockingDeque<>();
}
// 添加对象集
public void addFood(ObserverAnimal animal) {
list.add(animal);
}
// remove obj
// run business
public void run(Food food) {
notifies(food);
}
// 具体的通知或业务处理入口
public void notifies(Food food) {
Iterator<ObserverAnimal> iterator = list.iterator();
while (iterator.hasNext()) {
ObserverAnimal next = iterator.next();
next.response(food);
}
}
}
Run
import com.example.observerdemo.api13.observer.monitor.Cat;
import com.example.observerdemo.api13.observer.monitor.Dog;
import com.example.observerdemo.api13.observer.monitor.You;
import com.example.observerdemo.api13.param.Food;
import com.example.observerdemo.api13.param.ParamEnum;
import com.example.observerdemo.api13.subject.SubjectFood;
public class Init {
public static void main(String[] args) {
SubjectFood subjectFood = new SubjectFood();
subjectFood.addFood(new Cat());
subjectFood.run(new Food(ParamEnum.CAT.getType(), ParamEnum.CAT.getName()));
System.out.println("只打印一条数据, 因为数据发生变动时, 只有一只猫在监控.(只有一个对象在监控)");
System.out.println("------------------------");
subjectFood.addFood(new Dog());
subjectFood.run(new Food(ParamEnum.DOG.getType(), ParamEnum.DOG.getName()));
System.out.println("这次打印了两条数据, 因为又牵出来一条狗子也加入了监控的队伍. 黑求黑求~~~");
System.out.println("------------------------");
subjectFood.addFood(new You());
subjectFood.run(new Food(ParamEnum.OTHER.getType(), ParamEnum.OTHER.getName()));
System.out.println("这次打印三条数据, 因为又有新物种假如了监视队列 ~~");
System.out.println("------------------------");
// 这次拿了空饲料袋子诱惑
subjectFood.run(new Food("", null));
System.out.println("又打印了三条数据, 因为不管我的饲料怎么变, 只要我倒腾了饲料盒子, 监视盒子的小家伙就会受到刺激.");
}
}
业务代码
业务代码同上...
具体实现3 (Java实现中参数传递的两种方式)
下面是随手写的一篇小Demo
代码结构
被观察者
构建一个时刻受小弟保护的大哥, 大哥自己一旦觉察有危险, 即刻通知手底下护卫.
import java.util.Observable;
/**
* 观察者模式
* 这是一个大哥,他此时受各路黑道追杀.
* 他手底下的小弟一直暗处监视保护他, 大哥一旦感觉有风吹草动, 就通知小弟过来回首掏.
*/
public class EldestBrother extends Observable {
// 前来刺杀的人数.
private int sum;
public int getSum() {
return sum;
}
public void setSum(int sum) {
this.sum = sum;
}
// BlackJack 我是大哥, 发现可疑人员欲要行刺于我, 速来!
public void notifiesToBlackJack() {
super.setChanged();
super.notifyObservers(sum);
}
// 青龙 我是大哥, 有人要s我, 速来!
public void notifiesToQingLongGang() {
super.setChanged();
super.notifyObservers(sum);
}
// 黑龙 我是大哥, 有人要s我, 速来!
public void notifiesToBlackHouseGang() {
super.setChanged();
super.notifyObservers();
}
}
观察者
这是大哥手底下可放心调动的势力, 其中有两个帮派, 一个杀手.
import java.util.Observable;
import java.util.Observer;
/**
* 大哥手底下的金字号打手
* 时刻关注着大哥的变化.
* @Explain 参数传入时, Observable和object都可以获取到参数.
*/
public class BlackJack implements Observer {
/**
* 这是一只和大哥联系的飞哥.
* @param o 飞哥的左腿
* @param arg 飞哥的右腿
*/
@Override
public void update(Observable o, Object arg) {
// 分析飞哥的左腿上信件是否有大哥传信.
if (o instanceof EldestBrother) {
// 身份特殊, 分别时同大哥约定, 如遇危险, 只需告诉我至险人数, 我必风雨相救.
int sum = ((EldestBrother) o).getSum();
if (sum > 0) {
System.out.println("BlackJack: 大哥, 信件被雨水浸湿了! ");
} else {
System.out.println("BlackJack: 大哥吉祥! ");
}
}
// 分析右腿: 解密信件.
int sum = (int) arg;
if (sum > 0) {
System.out.println("BlackJack: 大哥, 收到你多次传信, 但是信件被加密无法破译! ");
} else {
System.out.println("BlackJack: 我乃金字号护卫, 大哥 别闹! ");
}
}
}
import java.util.Observable;
import java.util.Observer;
/**
* 青龙帮天机阁分部-红警阁
* 负责监听大哥的危急情况.
*/
public class QingLongGang implements Observer {
/**
* 单线通信大哥的小鸟.
* @param o 小鸟的左腿
* @param arg 小鸟的右腿
*/
@Override
public void update(Observable o, Object arg) {
// 解密信件.
int sum = (int) arg;
// 读取信件.
if (sum > 0) {
System.out.println("青龙: 大哥坚持, 青龙帮上下, 即刻杀到! ");
} else {
System.out.println("青龙: 大哥在上, 青龙帮上下向大哥问好. ");
}
// TODO... 右腿已废
}
}
import java.util.Observable;
import java.util.Observer;
/**
* 黑帮
* 摸摸保护大哥的一部分隐秘势力.
*/
public class BlackHouseGang implements Observer {
// 买香烟啦
@Override
public void update(Observable o, Object arg) {
/**
* 卖的什么烟啊? 龙婆...
*/
if (o instanceof EldestBrother) {
// 龙婆: 不知道数量的烟!!!
int sum = ((EldestBrother) o).getSum();
// 不好, 大哥有情况.
if (sum > 0) {
System.out.println("黑龙: 大哥, 黑帮杀到! ");
} else {
System.out.println("黑龙: 静默中. ");
}
}
}
}
业务处理
下面是一个简单的故事线
public class Init {
public static void main(String[] args) {
// new了一个大哥 南天一红
EldestBrother boss = new EldestBrother();
// 连线贴身护卫 BlackJack
boss.addObserver(new BlackJack());
// 测试一下护卫的忠心
System.out.println("测试一下BlackJack的忠心");
boss.setSum(0);
boss.notifiesToBlackJack();
// 再试一下护卫的忠心
System.out.println("再试一下BlackJack的忠心");
boss.setSum(2);
boss.notifiesToBlackJack();
System.out.println("经过一番试探, BlackJack很有可能已经被敌方买通. 八嘎牙路!!!");
System.out.println("----------------------------------------------------------");
// 连线左护法 青龙
boss.addObserver(new QingLongGang());
boss.setSum(2);
boss.notifiesToBlackJack();
System.out.println("大哥: BlackJack 你不用表水了, 我已知你心意, 你走吧!");
System.out.println("----------------------------------------------------------");
// 连线又护法 黑虎
boss.addObserver(new BlackHouseGang());
boss.setSum(0);
boss.notifiesToBlackJack();
System.out.println("青龙: BlackJack 你个叛徒, 还在这里逼逼叨, 受死!");
}
}