24种设计模式--观察者模式【Observer Pattern】
《孙子兵法》有云: “知彼知己,百战不殆;不知彼而知己,一胜一负;不知彼,不知己,每战必殆”,那怎么才能知己知彼呢?知己是很容易的,自己的军队嘛,很容易知道,那怎么知彼呢?安插间谍是很好的一个办法,我们今天就来讲一个间谍的故事。
韩非子大家都应该记得吧,法家的代表人物,主张建立法制社会,实施重罚制度,真是非常有远见呀,看看现在社会在呼吁什么,建立法制化的社会,在 2000 多年前就已经提出了。大家可能还不知道,法家还有一个非常重要的代表人物,李斯,对,就是李斯,秦国的丞相,最终被残忍的车裂的那位,李斯和韩非子都是荀子的学生,李斯是师兄,韩非子是师弟,若干年后,李斯成为最强诸侯秦国的上尉,致力于统一全国,于是安插了间谍到各个国家的重要人物的身边,以获取必要的信息,韩非子作为韩国的重量级人物,身边自然没少间谍了,韩非子早饭吃的什么,中午放了几个 P,晚上在做什么娱乐,李斯都了如指掌,那可是相隔千里!怎么做到的呢?间谍呀! 好,我们先通过程序把这个过程展现一下,看看李斯是怎么监控韩非子,先看类图:
看惯了清一色的谈黄色类图,咱换个颜色,写程序是一个艺术创作过程,我的一个同事就曾经把一个类图画成一个小乌龟的形状,超级牛 X。这个类图应该是程序员最容易想到得,你要监控,我就给你监控,正确呀,我们来看程序的实现,先看我们的主角韩非子的接口(类似于韩非子这样的人,被观察者角色):
package com.pattern.observer; /**
* 类似韩非子这样的人,被监控起来了还不知道
* @author http://www.cnblogs.com/initial-road/
*
*/
public interface IHanFeiZi { // 韩非子也是人,也要吃早饭的
public void haveBreakfast(); // 韩非子也是人,是人就要娱乐活动
public void haveFun(); } // 然后看韩非子的实现类 HanFeiZi.java: package com.pattern.observer; /**
* 韩非子,李斯的师弟,韩国的重要人物
* @author http://www.cnblogs.com/initial-road/
*
*/
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;
} 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;
} }
其中有两个 getter/setter 方法,这个就没有在类图中表示出来,比较简单,通过 isHaveBreakfast和 isHaveFun 这两个布尔型变量来判断韩非子是否在吃饭或者娱乐,韩非子是属于被观察者,那还有观察者李斯,我们来看李斯这类人接口:
package com.pattern.observer; /**
* 类似于李斯的这种人,现代嘛叫做偷窥狂
* @author http://www.cnblogs.com/initial-road/
*
*/
public interface ILiSi { // 一发现别人有动静,自己也要行动起来
public void update(String context); }
李斯这类人比较简单,一发现自己观察的对象发生了变化,比如吃饭,娱乐了,自己立刻也要行动起来,那怎么行动呢?看实现类:
package com.pattern.observer; /**
* 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
* @author http://www.cnblogs.com/initial-road/
*
*/
public class LiSi implements ILiSi { // 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
public void update(String context) {
System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");
this.reportToQinShiHuang(context);
System.out.println("李斯:汇报完毕,秦老板打赏他...\n");
} // 汇报秦始皇
private void reportToQinShiHuang(String reportContent){
System.out.println("李斯:报告,秦老板!韩非子有活动 ---> " + reportContent);
}
}
韩非子是秦始皇非常崇拜的人物,甚至说过见韩非子一面死又何憾!不过,韩非子还真是被秦始皇干掉的,历史呀上演过太多这样的悲剧。这么重要的人物有活动,你李斯敢不向老大汇报?!
两个重量级的人物都定义出来了,那我们就来看看要怎么监控,先写个监控程序:
package com.pattern.observer; /**
* 监控程序
* @author http://www.cnblogs.com/initial-road/
*
*/
public 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;
} public void run(){
while(true){
// 监控是否在吃早餐
if("breakfast".equals(this.type)){
// 如果发现韩非子在吃饭,就通知李斯
if(this.hanFeiZi.isHaveBreakfast()){
this.liSi.update("韩非子在吃饭");
// 重置状态,继续监控
this.hanFeiZi.setHaveBreakfast(false);
}
}else{ // 监控是否在娱乐
if(this.hanFeiZi.isHaveFun()){
this.liSi.update("韩非子在娱乐");
this.hanFeiZi.setHaveFun(false);
}
}
}
} }
监控程序继承了 java.lang.Thread 类,可以同时启动多个线程进行监控,Java 的多线程机制还是比较简单的,继承 Thread 类,重写 run()方法,然后 new SubThread(),再然后 subThread.start()就可以启动一个线程了,我们继续往下看:
package com.pattern.observer; /**
* 这个Client就是我们,用我们的视角看待这段历史
* @author http://www.cnblogs.com/initial-road/
*
*/
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(); // 然后这里我们看看韩非子在干什么
// 主线程等待1秒后再往下执行
Thread.sleep(1000);
hanFeiZi.haveBreakfast(); // 韩非子娱乐了
Thread.sleep(1000);
hanFeiZi.haveFun();
} }
结果出来,韩非子一吃早饭李斯就知道,韩非子一娱乐李斯也知道,非常正确!结果正确但并不表示你有成绩,我告诉你:你的成绩是 0,甚至是负的,你有没有看到你的 CPU 飙升,Eclipse 不响应状态?看到了?看到了你还不想为什么?!看看上面的程序,别的就不多说了,使用了一个 while(true)这样一个死循环来做监听,你要是用到项目中,你要多少硬件投入进来?你还让不让别人的程序也 run 起来?!一台服务器就跑你这一个程序就完事了,错,绝对的错!
错误也看到了,我们必须要修改,这个没有办法应用到项目中去呀,而且这个程序根本就不是面向对象的程序,这完全是面向过程的(我写出这样的程序也不容易呀,安慰一下自己),不改不行,怎么修改呢?我们来想,既然韩非子一吃饭李斯就知道了,那我们为什么不把李斯这个类聚集到韩非子这里类上呢?说改就改,立马动手,我们来看修改后的类图:
类图非常简单,就是在 HanFeiZi 类中引用了 IliSi 这个接口,看我们程序代码怎么修改,IhanFeiZi接口完全没有修改,我们来看 HanFeiZi 这个实现类:
package com.pattern.observer.advance; import com.pattern.observer.ILiSi;
import com.pattern.observer.LiSi; /**
* 韩非子,李斯的师弟,韩国的重要人物
* @author http://www.cnblogs.com/initial-road/
*
*/
public class HanFeiZi { // 把李斯声明出来
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()方法,完成李斯观察韩非子任务,李斯的接口和实现类都没有任何改变,我们再来看看 Client 程序的变更:
package com.pattern.observer.advance; /**
* 这个Client就是我们,用我们的视角看待这段历史
* @author http://www.cnblogs.com/initial-road/
*
*/
public class Client { public static void main(String[] args) {
// 定义出韩非子
HanFeiZi hanFeiZi = new HanFeiZi(); // 然后这里我们看看韩非子在干什么
hanFeiZi.haveBreakfast(); // 韩非子娱乐了
hanFeiZi.haveFun();
} }
李斯都不用在 Client 中定义了,非常简单。
运行结果正确,效率也比较高,是不是应该乐呵乐呵了?大功告成了?稍等等,你想在战国争雄的时候,韩非子这么有名望(法家代表)、有实力(韩国的公子,他老爹参与过争夺韩国王位)的人,就只有秦国一个国家关心他吗?想想也不可能呀,肯定有一大帮的各国的类似李斯这样的人在看着他,监视着一举一动,但是看看我们的程序,你在 HanFeiZi 这个类中定义:
一下子就敲死了,只有李斯才能观察到韩非子,这是不对的,也就是说韩非子的活动只通知了李斯一个人,这不可能;再者,李斯只观察韩非子的吃饭,娱乐吗?政治倾向不关心吗?思维倾向不关心吗?杀人放火不关心吗?也就说韩非子的一系列活动都要通知李斯,那可怎么办?要按照上面的例子,我们不是要修改疯掉了吗?这和开闭原则严重违背呀,我们的程序有问题,怎么修改,来看类图:
我们把接口名称修改了一下,这样显得更抽象化,Observable 是被观察者,就是类似韩非子这样的人,Observer 接口是观察者,类似李斯这样的,同时还有其他国家的比如王斯、刘斯等,在 Observable 接口中有三个比较重要的方法,分别是 addObserver 增加观察者,deleteObserver 删除观察者,notifyObservers通知所有的观察者,这是什么意思呢?我这里有一个信息,一个对象,我可以允许有多个对象来察看,你观察也成,我观察也成,只要是观察者就成,也就是说我的改变或动作执行,会通知其他的对象,看程序会更明白一点,先看 Observable 接口:
package com.pattern.observer.advance2; /**
* 所有被观察者,通用接口
* @author http://www.cnblogs.com/initial-road/
*
*/
public interface Observable { // 增加一个观察者
public void addObserver(Observer observer); // 删除一个观察者,我不想让你监控了
public void deleteObserver(Observer observer); // 既然要观察,我发生改变了,他也应该用所动作--通知观察者
public void notifyObservers(String context); } // 这是一个通用的被观察者接口,所有的被观察者都可以实现这个接口。再来看韩非子的实现类: package com.pattern.observer.advance2; import java.util.ArrayList; /**
* 韩非子,李斯的师弟,韩国的重要人物
* @author http://www.cnblogs.com/initial-road/
*
*/
public class HanFeiZi { // 定义一个变长数组,存放所有的观察者
private ArrayList<Observer> observerList = new ArrayList<Observer>(); // 增加观察者
public void addObserver(Observer observer){
this.observerList.add(observer);
} // 删除观察者
public void deleteObserver(Observer observer){
this.observerList.remove(observer);
} // 通知所有的观察者
public void notifyObserver(String context){
for(Observer observer : observerList){
observer.update(context);
}
} // 韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
// 通知所有的观察者
this.notifyObserver("韩非子在吃饭");
} // 韩非子开始娱乐了,古代人没啥娱乐
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
this.notifyObserver("韩非子在娱乐");
} } // 再来看观察者接口 Observer.java: package com.pattern.observer.advance2; /**
* 所有观察者,通用接口
* @author http://www.cnblogs.com/initial-road/
*
*/
public interface Observer { // 一发现别人有动静,自己也要行动起来
public void update(String context); } // 然后是三个很无耻的观察者,偷窥狂嘛: package com.pattern.observer.advance2; /**
* 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
* @author http://www.cnblogs.com/initial-road/
*
*/
public class LiSi implements Observer { // 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
public void update(String context) {
System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
this.reportQinShiHuang(context);
System.out.println("李斯:汇报完毕了,秦老板大个赏...\n");
} private void reportQinShiHuang(String reportContext){
System.out.println("李斯:报告,秦老板!韩非子有活动了 ---> " + reportContext);
}
} // 李斯是真有其人,以下两个观察者是杜撰出来的: package com.pattern.observer.advance2; /**
* 王斯,也是观察者,杜撰的人名
* @author http://www.cnblogs.com/initial-road/
*
*/
public class WangSi implements Observer { // 王斯,看到韩非子有活动,自己就受不了
public void update(String context) {
System.out.println("王斯:观察到韩非子有活动,自己也开始活动了...");
this.cry(context);
System.out.println("王斯:真的哭死了...\n");
} // 一看李斯有活动,就哭,痛苦
private void cry(String context){
System.out.println("王斯:因为" + context + ", --所以我悲伤呀!");
}
} package com.pattern.observer.advance2; /**
* 刘斯这个人,是个观察者,只要韩非子一有动静,这边就知道
* 杜撰的人名
* @author http://www.cnblogs.com/initial-road/
*
*/
public class LiuSi implements Observer { // 刘斯,观察到韩非子活动后,自己也做一定的事情
public void update(String context) {
System.out.println("刘斯:观察到韩非子活动,开始动作了...");
this.happy(context);
System.out.println("刘斯:真被乐死了\n");
} // 一看韩非子有变化,他就快乐
private void happy(String context){
System.out.println("刘斯:因为:" + context + ",---所以我快乐呀!");
}
} // 所有的历史人物都在场了,那我们来看看这场历史闹剧是如何演绎的: package com.pattern.observer.advance2; /**
* 这个Client就是我们,用我们的视角看待这段历史
* @author http://www.cnblogs.com/initial-road/
*
*/
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(); } }
好了,结果也正确了,也符合开闭原则了,也同时实现类间解耦,想再加观察者?好呀,继续实现Observer 接口就成了,这时候必须修改 Client 程序,因为你业务都发生了变化。
细心的你可能已经发现,HanFeiZi 这个实现类中应该抽象出一个父类,父类完全实现接口,HanFeiZi这个类只实现两个方法 haveBreakfast 和 haveFun 就可以了,是的,是的,确实是应该这样,那先稍等等,我 们 打 开 JDK 的 帮 助 文 件 看 看 , 查 找 一 下 Observable 是 不 是 已 经 有 这 个 类 了? JDK 中 提 供 了 :java.util.Observable 实现类和 java.util.Observer 接口,也就是 说我们上面 写的那个例 子中 的Observable 接口可以改换成java.util.Observale 实现类了,看如下类图:
是不是又简单了很多?那就对了!然后我们看一下我们程序的变更,先看 HanFeiZi 的实现类:
package com.pattern.observer.perfect; import java.util.Observable; /**
* 韩非子,李斯的师弟,韩国的重要人物
* @author http://www.cnblogs.com/initial-road/
*
*/
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 对象,删除了增加、删除观察者的方法,简单了很多,那我们再来看观察者的实现类:
package com.pattern.observer.perfect; import java.util.Observable;
import java.util.Observer; /**
* 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
* @author http://www.cnblogs.com/initial-road/
*
*/
public class LiSi implements Observer { // 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要想老板汇报
public void update(Observable observer, Object obj) {
System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
this.reportToQinShiHuang(obj.toString());
System.out.println("李斯:汇报完毕,秦老板打赏...\n");
} // 汇报给秦始皇
private void reportToQinShiHuang(String reportContext){
System.out.println("李斯:报告,秦老板!韩非子有活动了 --> " + reportContext);
} }
就改变了部分代码, 应该 java.util.Observer 接口要求 update 传递过来两个变量,Observable 这个变量我们没用到,就不处理了。其他两个观察者实现类也是相同的改动,如下代码:
package com.pattern.observer.perfect; import java.util.Observable;
import java.util.Observer; /**
* 王斯,也是观察者,杜撰的人名
* @author http://www.cnblogs.com/initial-road/
*
*/
public class WangSi implements Observer { // 王斯,看到韩非子有活动,自己就受不了
public void update(Observable observable, Object obj) {
System.out.println("王斯:观察到韩非子有活动了,自己也开始活动了...");
this.cry(obj.toString());
System.out.println("王斯:真真的哭死了...\n");
} // 一看李斯有活动,就哭,痛苦
private void cry(String context){
System.out.println("王斯:因为" + context + ", --所以我悲伤呀!");
} } package com.pattern.observer.perfect; import java.util.Observable;
import java.util.Observer; /**
* 刘斯这个人,是个观察者,只要韩非子一有动静,这边就知道
* 杜撰的人名
* @author http://www.cnblogs.com/initial-road/
*
*/
public class LiuSi implements Observer { // 刘斯,观察到韩非子活动后,自己也做一定的事情
public void update(Observable observable, Object obj) {
System.out.println("刘斯:观察到韩非子有活动,开始动作了...");
this.happy(obj.toString());
System.out.println("刘斯:真被乐死了\n");
} // 一看韩非子有变化,他就快乐
private void happy(String context){
System.out.println("刘斯:因为" + context + ", ---所以我快乐呀!");
}
} // 然后再来看 Client 程序: package com.pattern.observer.perfect; import java.util.Observer; /**
* 这个Client就是我们,用我们的视角看待这段历史
* @author http://www.cnblogs.com/initial-road/
*
*/
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(); } }
程序体内没有任何变更,只是引入了一个接口而已。
运行结果一样,只是通知的先后顺序不同而已,程序已经简约到极致了。以上讲解的就是观察者模式,这个模式的通用类图如下:
观察者模式在实际项目的应用中非常常见,比如你到 ATM 机器上取钱,多次输错密码,卡就会被 ATM吞掉,吞卡动作发生的时候,会触发哪些事件呢?第一摄像头连续快拍,第二,通知监控系统,吞卡发生;第三,初始化 ATM 机屏幕,返回最初状态,你不能因为就吞了一张卡,整个 ATM 都不能用了吧,一般前两个动作都是通过观察者模式来完成的。
观察者模式有一个变种叫做发布/订阅模型(Publish/Subscribe) ,如果你做过 EJB(EnterpriseJavaBean)的开发,这个你绝对不会陌生。EJB2是个折腾死人不偿命的玩意儿,写个 Bean 要实现,还要继承,再加上那一堆的配置文件,小项目还凑活,你要知道用 EJB 开发的基本上都不是小项目,到最后是每个项目成员都在骂 EJB 这个忽悠人的东西;但是 EJB3 是个非常优秀的框架,还是算比较轻量级,写个 Bean只要加个 Annotaion 就成了,配置文件减少了,而且也引入了依赖注入的概念,虽然只是 EJB2 的翻版,但是毕竟还是前进了一步,不知道以后 EJB 的路会怎么样。 在 EJB 中有三个类型的 Bean:Session Bean、EntityBean 和 MessageDriven Bean,我们这里来说一下 MessageDriven Bean(一般简称为 MDB),消息驱动 Bean,消息的发布者(Provider)发布一个消息,也就是一个消息驱动 Bean,通过 EJB 容器(一般是 Message Queue消息队列)通知订阅者做出回应,从原理上看很简单,就是观察者模式的升级版。
那观察者模式在什么情况下使用呢?观察者可以实现消息的广播,一个消息可以触发多个事件,这是观察者模式非常重要的功能。使用观察者模式也有两个重点问题要解决:广播链的问题。如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表 A 上写了一个触发器,内容是一个字段更新后更新表 B 的一条数据,而表 B 上也有个触发器,要更新表 C,表 C 也有触发器…,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一(传递两次),这还是比较好控制的;
异步处理问题。这个 EJB 是一个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个大家有时间看看 Message Queue,就会有更深的了解。
我们在来回顾一下我们写的程序,观察者增加了,我就必须修改业务逻辑 Client 程序,这个是必须得吗?回顾一下我们以前讲到工厂方法模式的时候用到了 ClassUtils 这个类,其中有一个方法就是根据接口查找到所有的实现类,问题解决了吧!我可以查找到所有的观察者,然后全部加进来,以后要是新增加观察者也没有问题呀,程序那真是一点都不用改了!
24种设计模式--观察者模式【Observer Pattern】的更多相关文章
- 23种设计模式--观察者模式-Observer Pattern
一.观察者模式的介绍 观察者模式从字面的意思上理解,肯定有两个对象一个是观察者,另外一个是被观察者,观察者模式就是当被观察者发生改变得时候发送通知给观察者,当然这个观察者可以是多个对象,在项 ...
- 设计模式-观察者模式(Observer Pattern)
观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 观察者 ...
- 设计模式 - 观察者模式(Observer Pattern) 详细说明
观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...
- 设计模式 - 观察者模式(Observer Pattern) 详细解释
观察者模式(Observer Pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...
- 设计模式 - 观察者模式(Observer Pattern) Java内置 用法
观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...
- C#设计模式——观察者模式(Observer Pattern)1
一.概述在软件设计工作中会存在对象之间的依赖关系,当某一对象发生变化时,所有依赖它的对象都需要得到通知.如果设计的不好,很容易造成对象之间的耦合度太高,难以应对变化.使用观察者模式可以降低对象之间的依 ...
- C#设计模式——观察者模式(Observer Pattern)
一.概述在软件设计工作中会存在对象之间的依赖关系,当某一对象发生变化时,所有依赖它的对象都需要得到通知.如果设计的不好,很容易造成对象之间的耦合度太高,难以应对变化.使用观察者模式可以降低对象之间的依 ...
- 二十四种设计模式:观察者模式(Observer Pattern)
观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...
随机推荐
- Error message “Assembly must be registered in isolation” when registering Plugins in Microsoft Dynamics CRM 2011 2013 解决办法
Error message “Assembly must be registered in isolation” when registering Plugins in Microsoft Dynam ...
- ssh -CT -o BatchMode=yes 用户名@主机名
- DevExpress GridControl 显示行号、设置行号宽
显示行号类 /// <summary> /// GridView 显示行号 设置行号列的宽度 /// </summary> /// <param name="g ...
- VC++ 统计文件夹下面的当日和本月生成的图片
void GetCapNum(int * todayNum, int * mouthNum) { string path = ".\\res";//路径位于程序运行目录下的r ...
- 动态调用DLL函数有时正常,有时报Access violation的异常
动态调用DLL函数有时正常,有时报Access violation的异常 typedef int (add *)(int a,int b); void test() { hInst=LoadL ...
- PHP获取当前url路径的函数及服务器变量
$_SERVER["QUERY_STRING"],$_SERVER["REQUEST_URI"],$_SERVER["SCRIPT_NAME" ...
- 让Windows Server 2008 + IIS 7+ ASP.NET 支持10万并发请求(转)
转自:http://www.cnblogs.com/dudu/archive/2009/11/10/1600062.html 今天下午17点左右,博客园博客站点出现这样的错误信息: Error Sum ...
- C、Shell、Perl基于Tomcat开发CGI程序环境配置
基于Tomcat7.0版本号配置CGI开发环境,步聚例如以下: 以我的Tomcat7安装文件夹为例:TOMCA_HOME = /Users/yangxin/Documents/devToos/java ...
- INSERT DELAYED 句法
INSERT 语句的 DELAYED 选项是一个 MySQL 特有的选项,如果你的客户端不能等待 INSERT 的完成,这将会是很有用的.This is a common problem when y ...
- 转:FORM:客制化Form的菜单栏和右鍵菜單
Oracle EBS还允许客制化Form的菜单栏. 用户最多可以定义45个form-level的trigger,名称必须为SPECIALn, 其中SPECIAL1 to SPECIAL15属于Tool ...