关于什么是Observer(观察者)模式,它的原理是什么,我不擅长理论知识的说明,这里通过一个简单有趣的小例子来给大家演示,相信大家看完之后,就会很明白了。
现在假设:一个小孩子在睡觉,睡醒后要他父亲来喂他吃东西,用代码怎么实现?
用面向对象的思维来考虑,至少要定义两个类,一个小孩类,一个父亲类,ok,看第一种实现方式,看代码
package demo1;
/**
* 第一种实现方式:让父亲主动的去观察小孩是否睡醒<br>
* 定义一个父亲类,并实现Runnable接口,让这个父亲类每个一段时间就去观察一下小孩是否醒来
* 定义一个小孩类,并实现Runnable接口,在run()方法中让小孩睡上一段时间,然后醒来
*
* @author jerry
*
*
*/
/**
* 小孩类
*/
class Child implements Runnable {
// 是否起床的标志
private boolean wakeup = false;
public boolean isWakeup() {
return wakeup;
}
public void setWakeup(boolean wakeup) {
this.wakeup = wakeup;
}
// 起床
public void wakenUp() {
this.wakeup = true;
}
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 模拟小孩睡5秒后起床
wakenUp();
}
}
/**
* 父亲类
*/
class Dad implements Runnable {
Child c;
public Dad(Child c) {
super();
this.c = c;
}
@Override
public void run() {
// 让父亲每个一秒就去看一下小孩是否睡醒
while (!c.isWakeup()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 如果睡醒,就喂小孩吃饭
feed(c);
}
private void feed(Child c) {
System.out.println("喂小孩");
}
}
public class Test1 {
public static void main(String[] args) {
Child c = new Child();
new Thread(c).start();
new Thread(new Dad(c)).start();
}
}
这样去实现代码,有很多不足的地方,比如很明显的一个,父亲要重复去观察小孩是否睡醒,不能去干其他事情,比如看巴西世界杯
为了改进这个问题,我们再来看第二种实现方式,看代码
package demo2;
/**
* 第二种实现方式:让小孩主动告知父亲,我睡醒,我要吃饭!<br>
* 定义一个父亲类,提供一个喂小孩的方法<br>
* 定义一个小孩类,并实现Runnable接口,在run()方法中让小孩睡上一段时间,然后醒来
*
* @author jerry
*
*/
class Child implements Runnable {
private Dad dad;
public Child(Dad dad) {
super();
this.dad = dad;
}
public void wakenUp() {
this.dad.feed(this);
}
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
wakenUp();
}
}
class Dad {
public void feed(Child c) {
System.out.println("喂小孩");
}
}
public class Test2 {
public static void main(String[] args) {
Dad dad = new Dad();
Child c = new Child(dad);
new Thread(c).start();
}
}
这回只要屁孩没醒,父亲就可以去做他想做的事情,可是这样还是有个问题。如果小孩是在不同时间段睡醒来,父亲要做出不同的事件处理
,就要在wakenUp里写大量的判断处理代码,然后调用父亲类中相应的处理方法,程序的可扩展性太差
为了改进这个问题,我们继续修改上边的代码,看第三种实现方式
package demo3;
/**
* 第三种实现方式:再定义一个事件类,用于记录处理起床的各种属性<br>
* 定义一个父亲类,提供一个处理小孩睡醒事件的方法<br>
* 定义一个小孩类,并实现Runnable接口,在run()方法中让小孩睡上一段时间,然后醒来
*
* @author jerry
*
*/
/**
* 事件类,用于记录处理起床的各种属性
*/
class WakenUpEvent {
private long time;// 起床事件
private String loc;// 起床地点
private Child source;// 事件源
public WakenUpEvent(long time, String loc, Child source) {
super();
this.time = time;
this.loc = loc;
this.source = source;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public Child getSource() {
return source;
}
public void setSource(Child source) {
this.source = source;
}
}
class Child implements Runnable {
private Dad dad;
public Child(Dad dad) {
super();
this.dad = dad;
}
public void wakenUp() {
// 将起床的事件传给父亲类相应的处理方法
this.dad.actionToWakenUp(new WakenUpEvent(System.currentTimeMillis(),
"bed", this));
}
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
wakenUp();
}
}
class Dad {
public void actionToWakenUp(WakenUpEvent event) {
System.out.println("time :" + event.getTime());
System.out.println("loc :" + event.getLoc());
// TODO 根据event里边的属性做不同的操作
}
}
public class Test3 {
public static void main(String[] args) {
Dad dad = new Dad();
Child c = new Child(dad);
new Thread(c).start();
}
}
这样代码看起来就爽多了,可是呢,屁孩有一天他不仅要他爸喂他,还要他妈抱他
。那就要在Child类中添加母亲的引用,重写wakenUp方法,程序扩展性还是太差
怎么办,进行修改实现方式,看第四种
package demo4;
import java.util.ArrayList;
import java.util.List;
/**
* 第四种实现方式:使用bserver模式实现<br>
* 定义一个起床事件监听器接口
* 定义一个父亲类,继承起床事件监听器,监听小孩起床事件<br>
* 定义一个母亲类,继承起床事件监听器,监听小孩起床事件<br>
* 定义一个小孩类,并实现Runnable接口,在run()方法中让小孩睡上一段时间,然后醒来
*
* @author jerry
*
*/
class WakenUpEvent {
private long time;
private String loc;
private Child source;
public WakenUpEvent(long time, String loc, Child source) {
super();
this.time = time;
this.loc = loc;
this.source = source;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public Child getSource() {
return source;
}
public void setSource(Child source) {
this.source = source;
}
}
/**
* 小孩子类
*/
class Child implements Runnable {
// 起床事件监听器
private List<WakenUpEventListener> listeners = new ArrayList<WakenUpEventListener>();
// 注册起床事件的监听
public void setOnWakenUpEventListener(WakenUpEventListener l) {
listeners.add(l);
}
// 起床事件
void wakenUp() {
for (WakenUpEventListener listener : listeners) {
// 回调起床事件监听器中的处理起床事件的方法
listener.onWakenUp(new WakenUpEvent(System.currentTimeMillis(),
"bed", this));
}
}
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
wakenUp();
}
}
/**
* 父亲类,继承起床事件监听器,监听小孩起床事件
*/
class Dad implements WakenUpEventListener {
@Override
public void onWakenUp(WakenUpEvent event) {
System.out.println("喂小孩");
}
}
/**
* 母亲类,继承起床事件监听器,监听小孩起床事件
*/
class Mother implements WakenUpEventListener {
public void onWakenUp(WakenUpEvent event) {
System.out.println("抱小孩");
}
}
/**
* 起床事件监听器接口
*/
interface WakenUpEventListener {
public void onWakenUp(WakenUpEvent event);
}
public class Test4 {
public static void main(String[] args) {
Dad dad = new Dad();
Mother mother = new Mother();
Child c = new Child();
c.setOnWakenUpEventListener(dad);
c.setOnWakenUpEventListener(mother);
new Thread(c).start();
}
}
这就是观测者模式!比起第三种实现方式,非常明显的好处就是,当小孩需要更多的人相应他的起床事件的时候,我们只需要定义相应的类,然后去继承WakenUpEventListener接口并重写它里边的方法,就可以实现睡醒事件的监听,而不用去修改Child类中的代码。因此代码的可扩展性是很爽的