本文是自己的学习笔记,主要参考以下资料
- 大话设计模式,程杰著,清华大学出版社出版
- 马士兵教育
1、原始的观察者代码
设想一个场景,用代码模拟照看小孩,当小孩哭的时候就安慰小孩。
class Child {
private boolean cry = false;
public boolean isCrying() {
return cry;
}
public void wakeUp() {
cry = true;
}
}
最直接的做法就是while循环,直到某个变量发生变化。
public Child child;
public static void main(String[] args) {
while(true) {
if(child.isCrying()) {
//do something
}
}
}
当其它的线程调用wakeUp()
方法时,程序自然就监控到孩子正在哭然后做对应的操作。
2、被观察者通知观察者
但是这样不断地while循环很占资源。我们换一个版本的代码,在其中加入观察者。并且不是观察者主动观察被监视对象,而是被监视对象发生动作时主动通知观察者。
class Dad {
public void feed(){
//feed
}
}
class Child {
private Dad dad = new Dad();
private boolean cry = false;
public boolean isCrying() {
return cry;
}
public void wakeUp() {
cry = true;
dad.feed();
}
}
这样就不用while循环监视孩子是否在哭,将处理逻辑和wakeUp()
方法强绑定,只要调用wakeUp()
方法,自然就会调用对应的事件处理逻辑。
但这样的弊端也显而易见,一个被观察对象可以用多个观察者,当我们想要多加入一个观察者时就显得很费劲。比如再加入观察者Mon和Dog。
class Dad { public void feed(){//feed}}
class Mon { public void hug(){//hug}}
class Dog { public void bark(){//bark}}
class Child {
private Dad dad = new Dad();
private Mon mon = new Mon();
private Dog dog = new Dog();
private boolean cry = false;
public boolean isCrying() {
return cry;
}
public void wakeUp() {
cry = true;
dad.feed();
mon.hug();
dog.bark();
}
}
3、观察者处理逻辑与被观察者解耦
我们不想让观察者的观察到事件后的处理逻辑和被观察者的行为强耦合,办法也很简单,和大多数设计模式一样。我们定义接口抽象观察者行为。被观察者维护一个集合,集合里存储观察着它的对象。当被观察者发生特定事件时,遍历通知集合里的观察者。
观察者的代码
interface Observer {
void actionOnWakeUp();
}
class Dad implements Observer {
void actionOnWakeUp(){System.out.println("feeding...");}
}
class Mon implements Observer {
void actionOnWakeUp(){System.out.println("hugging...");}
}
class Dog implements Observer {
void actionOnWakeUp(){System.out.println("barking...");}
}
被观察者
class Child{
private List<Observer> observerList = new ArrayList<>();
// 省略observerList的add, delete等方法
private boolean cry = false;
public void wakeUp() {
cry = true;
for(Observer observer: observerList) {
observer.actionOnWakeUp();
}
}
}
这样就能实现观察者和被观察对象的行为解耦。
但这样还不完美,因为观察者应该要根据不同的情况对被观察者的动作作出反应。比如孩子哭了是在什么时候哭,是刚刚吃完饭哭,还是半夜哭,还是自己玩的时候跌倒了哭。不同的情况应该有不同的处理逻辑。
要想知道具体情况,那就是在方法里传参。我们需要定义一个事件类,里面描述我们需要的一切事件元信息。
代码如下,省略了基本重复的部分。
interface Observer {
void actionOnWakeUp(Event event);
}