设计模式之禅-观察者模式
1.业务背景
知彼知己,百战不殆;不知彼而知己,一胜一负;不知彼,不知己,每战必殆。
李斯和韩非子都是荀子的学生,李斯是师兄,韩非子是师弟,若干年后,李斯成为最强诸侯秦国的上尉,致力于统一全国,于是安插了间谍到各个国家的重要人物的身边,以获取必要的信息,韩非子作为韩国的重量级人物,身边自然没少间谍了。我们先通过程序把这个过程展现一下,看看李斯是怎么监控韩非子,先看类图:
2.先看我们的主角韩非子的接口(类似于韩非子这样的人,被观察者角色):
public interface IHanFeiZi {
//韩非子也是人,也要吃早饭的
public void haveBreakfast();
//韩非之也是人,是人就要娱乐活动
public void haveFun();
}
韩非子的实现类 HanFeiZi.java:
public class HanFeiZi implements IHanFeiZi{
//韩非子是否在吃饭,作为监控的判断标准
private boolean isHaveBreakfast = false;
//韩非子是否在娱乐
private boolean isHaveFun = false;
//韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
this.isHaveBreakfast =true;
}
//韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
this.isHaveFun = true;
}
//以下是bean的基本方法,getter/setter,不多说
public boolean isHaveBreakfast() {
return isHaveBreakfast;
}
public void setHaveBreakfast(boolean isHaveBreakfast) {
this.isHaveBreakfast = isHaveBreakfast;
}
public boolean isHaveFun() {
return isHaveFun;
}
public void setHaveFun(boolean isHaveFun) {
this.isHaveFun = isHaveFun;
}
}
通过 isHaveBreakfast和 isHaveFun 这两个布尔型变量来判断韩非子是否在吃饭或者娱乐
李斯这类人的接口:
public interface ILiSi {
//一发现别人有动静,自己也要行动起来
public void update(String context);
}
李斯这类人比较简单,一发现自己观察的对象发生了变化,比如吃饭,娱乐了,自己立刻也要行动起来,那怎么行动呢?看实现类:
public class LiSi implements ILiSi{
//首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
public void update(String str){
System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");
this.reportToQiShiHuang(str);
System.out.println("李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...\n");
}
//汇报给秦始皇
private void reportToQiShiHuang(String reportContext){
System.out.println("李斯:报告,秦老板!韩非子有活动了--->"+reportContext);
}
}
两个重量级的人物都定义出来了,那我们就来看看要怎么监控,先写个监控程序:
class Watch extends Thread{
private HanFeiZi hanFeiZi;
private LiSi liSi;
private String type;
//通过构造函数传递参数,我要监控的是谁,谁来监控,要监控什么
public Watch(HanFeiZi _hanFeiZi,LiSi _liSi,String _type){
this.hanFeiZi =_hanFeiZi;
this.liSi = _liSi;
this.type = _type;
}
@Override
public void run(){
while(true){
if(this.type.equals("breakfast")){ //监控是否在吃早餐
//如果发现韩非子在吃饭,就通知李斯
if(this.hanFeiZi.isHaveBreakfast()){
this.liSi.update("韩非子在吃饭");
//重置状态,继续监控
this.hanFeiZi.setHaveBreakfast(false);
}
}else{//监控是否在娱乐
if(this.hanFeiZi.isHaveFun()){
this.liSi.update("韩非子在娱乐");
this.hanFeiZi.setHaveFun(false);
}
}
}
}
}
这个Client就是我们,用我们的视角看待这段历史
public class Client {
public static void main(String[] args) throws InterruptedException {
//定义出韩非子和李斯
LiSi liSi = new LiSi();
HanFeiZi hanFeiZi = new HanFeiZi();
//观察早餐
Watch watchBreakfast = new Watch(hanFeiZi,liSi,"breakfast");
//开始启动线程,监控
watchBreakfast.start();
//观察娱乐情况
Watch watchFun = new Watch(hanFeiZi,liSi,"fun");
watchFun.start();
//然后这里我们看看韩非子在干什么
Thread.sleep(1000); //主线程等待1秒后后再往下执行
hanFeiZi.haveBreakfast();
//韩非子娱乐了
Thread.sleep(1000);
hanFeiZi.haveFun();
}
}
运行结果如下:
韩非子:开始吃饭了...
李斯:观察到李斯活动,开始向老板汇报了...
李斯:报告,秦老板!韩非子有活动了--->韩非子在吃饭
李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...
韩非子:开始娱乐了...
李斯:观察到李斯活动,开始向老板汇报了...
李斯:报告,秦老板!韩非子有活动了--->韩非子在娱乐
李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...
上面的程序,使用了一个 while(true)这样一个死循环来做监听,一台服务器就跑你这一个程序就完事了,错,绝对的错!
既然韩非子一吃饭李斯就知道了,那我们为什么不把李斯这个类聚集到韩非子这里类上呢
public class HanFeiZi implements IHanFeiZi{
//把李斯声明出来
private ILiSi liSi =new LiSi();
//韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
//通知李斯
this.liSi.update("韩非子在吃饭");
}
//韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
this.liSi.update("韩非子在娱乐");
}
}
韩非子 HanFeiZi 实现类就把接口的两个方法实现就可以了,在每个方法中调用 LiSi.update()方法,完成李斯观察韩非子任务,李斯的接口和实现类都没有任何改变
public class Client {
public static void main(String[] args) {
//定义出韩非子
HanFeiZi hanFeiZi = new HanFeiZi();
//然后这里我们看看韩非子在干什么
hanFeiZi.haveBreakfast();
//韩非子娱乐了
hanFeiZi.haveFun();
}
}
李斯都不用在 Client 中定义了,非常简单。
韩非子这么有名望(法家代表)、有实力(韩国的公子,他老爹参与过争夺韩国王位)的人,就只有秦国一个国家关心他吗?想想也不可能呀,肯定有一大帮的各国的类似李斯这样的人在看着他,监视着一举一动。再者,李斯只观察韩非子的吃饭,娱乐吗?政治倾向不关心吗?思维倾向不关心吗?
Observable 是被观察者,就是类似韩非子这样的人,Observer 接口是观察者,类似李斯这样的,在 Observable 接口中有三个比较重要的方法,分别是 addObserver 增加观察者,deleteObserver 删除观察者,notifyObservers通知所有的观察者。
所有被观察者者,通用接口
public interface Observable {
//增加一个观察者
public void addObserver(Observer observer);
//删除一个观察者,——我不想让你看了
public void deleteObserver(Observer observer);
//既然要观察,我发生改变了他也应该用所动作——通知观察者
public void notifyObservers(String context);
}
看韩非子的实现类:
public class HanFeiZi implements Observable{
//定义个变长数组,存放所有的观察者
private ArrayListobserverList = new ArrayList();
//增加观察者
public void addObserver(Observer observer){
this.observerList.add(observer);
}
//删除观察者
public void deleteObserver(Observer observer){
this.observerList.remove(observer);
}
//通知所有的观察者
public void notifyObservers(String context){
for(Observer observer:observerList){
observer.update(context);
}
}
//韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
//通知所有的观察者
this.notifyObservers("韩非子在吃饭");
}
//韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
this.notifyObservers("韩非子在娱乐");
}
}
再来看观察者接口 Observer.java:
public interface Observer {
//一发现别人有动静,自己也要行动起来
public void update(String context);
}
李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
public class LiSi implements Observer{
public void update(String str){
System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
this.reportToQiShiHuang(str);
System.out.println("李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...\n");
}
//汇报给秦始皇
private void reportToQiShiHuang(String reportContext){
System.out.println("李斯:报告,秦老板!韩非子有活动了--->"+reportContext);
}
}
王斯,也是观察者
public class WangSi implements Observer{
//王斯,看到韩非子有活动,自己就受不了
public void update(String str){
System.out.println("王斯:观察到韩非子活动,自己也开始活动了...");
this.cry(str);
System.out.println("王斯:真真的哭死了...\n");
}
//一看李斯有活动,就哭,痛哭
private void cry(String context){
System.out.println("王斯:因为"+context+",——所以我悲伤呀!");
}
}
刘斯这个人,是个观察者
public class LiuSi implements Observer{
//刘斯,观察到韩非子活动后,自己也做一定得事情
public void update(String str){
System.out.println("刘斯:观察到韩非子活动,开始动作了...");
this.happy(str);
System.out.println("刘斯:真被乐死了\n");
}
//一看韩非子有变化,他就快乐
private void happy(String context){
System.out.println("刘斯:因为" +context+",——所以我快乐呀!" );
}
}
public class Client {
public static void main(String[] args) {
//三个观察者产生出来
Observer liSi = new LiSi();
Observer wangSi = new WangSi();
Observer liuSi = new LiuSi();
//定义出韩非子
HanFeiZi hanFeiZi = new HanFeiZi();
//我们后人根据历史,描述这个场景,有三个人在观察韩非子
hanFeiZi.addObserver(liSi);
hanFeiZi.addObserver(wangSi);
hanFeiZi.addObserver(liuSi);
//然后这里我们看看韩非子在干什么
hanFeiZi.haveBreakfast();
}
}
运行结果如下:
韩非子:开始吃饭了...
李斯:观察到李斯活动,开始向老板汇报了...
李斯:报告,秦老板!韩非子有活动了--->韩非子在吃饭
李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...
王斯:观察到韩非子活动,自己也开始活动了...
王斯:因为韩非子在吃饭,——所以我悲伤呀!
王斯:真真的哭死了...
刘斯:观察到韩非子活动,开始动作了...
刘斯:因为韩非子在吃饭,——所以我快乐呀!
刘斯:真被乐死了
HanFeiZi 这个实现类中应该抽象出一个父类,父类完全实现接口,HanFeiZi这个类只实现两个方法 haveBreakfast 和 haveFun 就可以了
HanFeiZi 的实现类:
public class HanFeiZi extends Observable{
//韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
//通知所有的观察者
super.setChanged();
super.notifyObservers("韩非子在吃饭");
}
//韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
super.setChanged();
this.notifyObservers("韩非子在娱乐");
}
}
改变的不多,引入了一个 java.util.Observable 对象,删除了增加、删除观察者的方法
我们再来看观察者的实现类:
public class LiSi implements Observer{
//首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
public void update(Observable observable,Object obj){
System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
this.reportToQiShiHuang(obj.toString());
System.out.println("李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...\n");
}
//汇报给秦始皇
private void reportToQiShiHuang(String reportContext){
System.out.println("李斯:报告,秦老板!韩非子有活动了--->"+reportContext);
}
}
然后再来看 Client 程序:
public class Client {
public static void main(String[] args) {
//三个观察者产生出来
Observer liSi = new LiSi();
Observer wangSi = new WangSi();
Observer liuSi = new LiuSi();
//定义出韩非子
HanFeiZi hanFeiZi = new HanFeiZi();
//我们后人根据历史,描述这个场景,有三个人在观察韩非子
hanFeiZi.addObserver(liSi);
hanFeiZi.addObserver(wangSi);
hanFeiZi.addObserver(liuSi);
//然后这里我们看看韩非子在干什么
hanFeiZi.haveBreakfast();
}
}
程序体内没有任何变更,只是引入了一个接口而已
观察者模式,这个模式的通用类图如下: