概述
观察者模式(有时又被称为发布/订阅模式)是软体设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。
先看一个例子
有个小孩在睡觉,醒来之后要喂奶。
我们使用的是java,所以不要闹出下面的笑话(披着面向对象的面向过程):
public class simulation {
public static void main(string... args) {
//小孩睡觉
//起来之后爸爸喂奶
//...
}
}
我们根据面向对象思想,加上多线程模拟child和dad,小孩在睡觉,随时可以起来,dad隔一段时间看下小孩是否醒来。
package observer;
import java.util.random;
class child implements runnable {
public static random r = new random();
private boolean wake = false;
public child() {
new thread(this).start();
}
@override
public void run() {
while (!wake) {
system.out.println("child:i am sleeping...");
try {
thread.sleep(1000);
} catch (interruptedexception e) {
e.printstacktrace();
}
if (r.nextint(10) > 8) {
wakeup();
}
}
}
public void wakeup() {
wake = true;
}
public boolean iswake() {
return wake;
}
}
class dad implements runnable {
private child c;
public dad(child c) {
new thread(this).start();
this.c = c;
}
@override
public void run() {
while (!c.iswake()) {
system.out.println("dad:child is sleeping...");
try {
thread.sleep(1000);
} catch (interruptedexception e) {
e.printstacktrace();
}
}
feed(c);
}
private void feed(child c2) {
system.out.println("feed child!");
}
}
public class firstquestion {
public static void main(string[] args) {
new dad(new child());
}
}
这样可以实现功能,但造成资源的浪费。开了这么多线程,dad时间都用在看小孩身上了,dad下午打牌的计划泡汤了。我们可以很容易的把dad解放出来:
class child implements runnable {
public static random r = new random();
private dad d;
private boolean wake = false;
public child(dad d) {
this.d = d;
}
@override
public void run() {
while (!wake) {
system.out.println("child:i am sleeping...");
try {
thread.sleep(1000);
} catch (interruptedexception e) {
e.printstacktrace();
}
if (r.nextint(10) > 8) {
wakeup();
}
}
}
public void wakeup() {
wake = true;
d.feed(this);
}
public boolean iswake() {
return wake;
}
}
class dad {
public void feed(child c) {
system.out.println("feed child!");
}
}
public class firstquestion {
public static void main(string[] args) {
new thread(new child(new dad())).start();
}
}
这样基本已经实现功能,稍微完善下,假如想知道小孩什么时候起来等一些信息,如果我们写在小孩类中就不太合适,所以我们抽象出类中wakenupevent:
class wakenupevent {
private date date;
private string loc;
private dad dad;
public wakenupevent(date date, string loc, dad dad) {
super();
this.date = date;
this.loc = loc;
this.dad = dad;
}
}
class child implements runnable {
private dad d;
public child(dad d) {
this.d = d;
}
@override
public void run() {
try {
thread.sleep(5000);
} catch (interruptedexception e) {
e.printstacktrace();
}
wakeup();
}
public void wakeup() {
d.actiontowakenup(new wakenupevent(new date(), "child", d));
}
}
class dad {
public void actiontowakenup(wakenupevent wakenupevent) {
system.out.println("child feed!");
}
}
public class firstquestion {
public static void main(string[] args) {
new thread(new child(new dad())).start();
}
}
这样dad可以做自己事,只要听到孩子声音,就过来喂奶。似乎问题已经解决,假如小孩醒后,小孩的爷爷想抱下,小孩家的小狗要叫下,等等,如果按照上面的设计,小孩需要持有爷爷grandfather、狗dog等的引用,再调用用响应的处理方法…需要修改较大的篇幅。实际上我们可以在小孩中使用一个集合存储所有监听小孩的对象,当小孩醒后,小孩依次调用监听者处理方法。要实现统一的接口,以可以被小孩监听器集合引用和调用相应方法,我们使用接口interface。
class wakenupevent {
private date date;
private string eventtype;
private object source;
public wakenupevent(date date, string eventtype, object source) {
this.date = date;
this.eventtype = eventtype;
this.source = source;
}
}
class child implements runnable {
private list list = new arraylist();
public void addwakenuplistener(wakenuplistener l) {
list.add(l);
}
@override
public void run() {
system.out.println("child is sleeping...");
try {
thread.sleep(2000);
} catch (interruptedexception e) {
e.printstacktrace();
}
wakeup();
}
public void wakeup() {
for (wakenuplistener l : list) {
l.actionwakenup(new wakenupevent(new date(), "", this));
}
}
}
interface wakenuplistener {
void actionwakenup(wakenupevent e);
}
class dad implements wakenuplistener {
@override
public void actionwakenup(wakenupevent e) {
system.out.println("dad feed child!");
}
}
class grandfather implements wakenuplistener {
@override
public void actionwakenup(wakenupevent e) {
system.out.println("grandfather holl child!");
}
}
public class firstquestion {
public static void main(string[] args) {
child c = new child();
c.addwakenuplistener(new dad());
c.addwakenuplistener(new grandfather());
new thread(c).start();
}
}
这个时候再看概述的例子,比较容易理解了吧!
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。[gof 《设计模式》]
观察者模式中的推模式与拉模式[摘录]
在observer模式中区分推模式和拉模式,先简单的解释一下两者的区别:推模式是当有消息时,把消息信息以参数的形式传递(推)给所有观察者,而拉模式是当有消息时,通知消息的方法本身并不带任何的参数,是由观察者自己到主体对象那儿取回(拉)消息。知道了这一点,大家可能很容易发现上面我所举的例子其实是一种推模式的observer模式。我们先看看这种模式带来了什么好处:当有消息时,所有的观察者都会直接得到全部的消息,并进行相应的处理程序,与主体对象没什么关系,两者之间的关系是一种松散耦合。但是它也有缺陷,第一是所有的观察者得到的消息是一样的,也许有些信息对某个观察者来说根本就用不上,也就是观察者不能“按需所取”;第二,当通知消息的参数有变化时,所有的观察者对象都要变化。鉴于以上问题,拉模式就应运而生了,它是由观察者自己主动去取消息,需要什么信息,就可以取什么,不会像推模式那样得到所有的消息参数。ok,说到这儿,你是否对于推模式和拉模式有了一点了解呢?
实际上上面的代码中,因java中awt事件的影响,我在event中加入了source字段,这算是拉模式的一种体现。我们可以得到公共的事件信息,也可以通过source得到发出事件对象的信息。
awt事件模拟
说到awt事件,我们根据上面的思路模拟下awt事件处理,观察者模式实现awt事件功能更加简单优雅,然而真正的awt也需要windows本身的事件驱动的支持,比如你按下某个button,首先windows捕获这个消息,把消息分发给java虚拟机,虚拟机在调用button相应的处理,button调用监听器处理(个人理解)。一般awt事件处理:
public class awtbutton extends frame {
public void lanch() {
button b = new button("test");
b.addactionlistener(new myactionlistener());
b.addactionlistener(new myactionlistener1());
this.add(b);
this.addwindowlistener(new windowadapter() {
@override
public void windowclosing(windowevent e) {
system.exit(0);
}
});
setsize(100, 100);
setvisible(true);
}
public static void main(string[] args) {
new awtbutton().lanch();
}
class myactionlistener implements actionlistener {
@override
public void actionperformed(actionevent e) {
system.out.println("button pressed!");
}
}
class myactionlistener1 implements actionlistener {
@override
public void actionperformed(actionevent e) {
system.out.println("button pressed1!");
}
}
}
结合上面的孩子的例子,我们使用控制台模拟awt事件:
public class simulationawtbutton {
public static void main(string[] args) {
button b = new button();
b.addactionlistener(new myactionlistener1());
b.addactionlistener(new myactionlistener2());
b.pressed();
}
}
class button {
private list.net设计模式(19):观察者模式(observer pattern)(部分摘录原文)
java尚学堂马士兵设计模式
作者:syxchina
出处:http://syxchina.cnblogs.com、
本文版权归作者、博客园和百度空间共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则作者会诅咒你的。
标签: 设计模式, design pattern
绿色通道:好文要顶关注我收藏该文与我联系
======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/