设计模式之禅之设计模式-观察者模式

一:观察者模式的定义
        --->观察者模式(Observer Pattern)也叫做发布订阅模式(Publish/subscribe),它是一个在项目中经常使用的模式
        --->定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新

二:观察者模式的角色

● Subject被观察者
        定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
● Observer观察者
        观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。
● ConcreteSubject具体的被观察者
        定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
● ConcreteObserver具体的观察者
        每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。

三:观察者模式的应用

观察模式的优点
● 观察者和被观察者之间是抽象耦合
        如此设计,则不管是增加观察者还是被观察者都非常容易扩展,而且在Java中都已经实现的抽象层级的定义,在系统扩展方面更是得心应手。
● 建立一套触发机制
        根据单一职责原则,每个类的职责是单一的,那么怎么把各个单一的职责串联成真实世界的复杂的逻辑关系呢?比如,我们去打猎,打死了一只母鹿,母鹿有三个幼崽,因失去了母鹿而饿死,尸体又被两只秃鹰争抢,因分配不均,秃鹰开始斗殴,然后羸弱的秃鹰死掉,生存下来的秃鹰,则因此扩大了地盘......这就是一个触发机制,形成了一个触发链。观察者模式可以完美地实现这里的链条形式。

观察模式的缺点
        ●观察者模式需要考虑一下开发效率和运行效率问题,一个被观察者,多个观察者,开发和调试就会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。
        ●多级触发时的效率更是让人担忧,大家在设计时注意考虑

观察模式的使用场景
● 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
● 事件多级触发场景。
● 跨系统的消息交换场景,如消息队列的处理机制。

观察模式的注意事项
● 广播链的问题
        --->在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),这
还是比较好控制的。
● 异步处理问题
        --->这个EJB是一个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个大家有时间看看Message Queue,就会有更深的了解。

四:观察者模式的实践
● 观察者和被观察者之间的消息沟通
        --->观察者中的update方法接受两个参数,一个是被观察者,一个是DTO(Data Transfer Object,据传输对象),DTO一般是一个纯洁的JavaBean,由被观察者生成,由观察者消费。

● 观察者响应方式
        --->在一个观察者多个被观察者的情况下,性能就需要提到日程上来考虑了,为什么呢?如果观察者来不及响应,被观察者的执行时间是不是也会被
拉长?那现在的问题就是:观察者如何快速响应?有两个办法:一是采用多线程技术,甭管是被观察者启动线程还是观察者启动线程,都可以明显地提高系统性能,这也就是大家通常所说的异步架构;二是缓存技术,甭管你谁来,我已经准备了足够的资源给你了,我保证快速响应,这当然也是一种比较好方案,代价就是开发难度很大,而且压力测试要做的足够充分,这种方案也就是大家说的同步架构。

● 被观察者尽量自己做主
        --->这是什么意思呢?被观察者的状态改变是否一定要通知观察者呢?不一定吧,在设计的时候要灵活考虑,否则会加重观察者的处理逻辑,一般是这样做的,对被观察者的业务逻辑doSomething方法实现重载,如增加一个doSomething(boolean isNotifyObs)方法,决定是否通知观察者,而不是在消息到达观察者时才判断是否要消费。

五:观察者模式的实例
● 文件系统
        比如,在一个目录下新建立一个文件,这个动作会同时通知目录管理器增加该目录,并通知磁盘管理器减少1KB的空间,也就说“文件”是一个被观察者,“目录管理器”和“磁盘管理器”则是观察者。
● 猫鼠游戏
        夜里猫叫一声,家里的老鼠撒腿就跑,同时也吵醒了熟睡的主人,这个场景中,“猫”就是被观察者,老鼠和人则是观察者。
● ATM取钱
        比如你到ATM机器上取钱,多次输错密码,卡就会被ATM吞掉,吞卡动作发生的时候,会触发哪些事件呢?第一,摄像头连续快拍,第二,通知监控系统,吞卡发生;第三,初始化ATM机屏幕,返回最初状态。一般前两个动作都是通过观察者模式来完成的,后一个动作是异常来完成。
● 广播收音机
        电台在广播,你可以打开一个收音机,或者两个收音机来收听,电台就是被观察者,收音机就是观察者。

六:观察者模式的案例

<一>李斯监视韩非子,利用监视线程的案例(不符合观察者模式的版型,但是一种非面向对象的思想)
【1】韩非子接口(被观察者接口)

 1 package com.yeepay.sxf.template17;
 2 /**
 3  * 被观察者的接口
 4  *
 5  * 韩非子
 6  * @author sxf
 7  *
 8  */
 9 public interface IHanFeiZi {
10
11     //韩非子要吃早饭
12     public void haveBreakFast();
13     //韩非子要娱乐活动
14     public void haveFun();
15 }

View Code

【2】韩非子实现类(被观察者实现类)

 1 package com.yeepay.sxf.template17;
 2 /**
 3  * 被观察接口的实现类
 4  * @author sxf
 5  *
 6  */
 7 public class HanFeiZi implements IHanFeiZi {
 8
 9     //韩非子是否在吃饭
10     private boolean isHavingBreakFast=false;
11     //韩非子是否在娱乐
12     private boolean isHavingFun=false;
13
14     @Override
15     public void haveBreakFast() {
16         System.out.println("HanFeiZi.haveBreakFast()韩非子在吃饭");
17         this.setHavingBreakFast(true);
18     }
19
20     @Override
21     public void haveFun() {
22         System.out.println("HanFeiZi.haveFun()韩非在在娱乐");
23         this.setHavingFun(true);
24         System.out.println("HanFeiZi.haveFun(sssssssssssssss)");
25     }
26
27     public synchronized  boolean isHavingBreakFast() {
28         return isHavingBreakFast;
29     }
30
31     public synchronized void setHavingBreakFast(boolean isHavingBreakFast) {
32         this.isHavingBreakFast = isHavingBreakFast;
33     }
34
35     public boolean isHavingFun() {
36         return isHavingFun;
37     }
38
39     public void setHavingFun(boolean isHavingFun) {
40         this.isHavingFun = isHavingFun;
41     }
42
43
44
45
46 }

View Code

【3】李斯接口(观察者接口)

 1 package com.yeepay.sxf.template17;
 2 /**
 3  * 观察者接口
 4  * 李斯
 5  * @author sxf
 6  *
 7  */
 8 public interface ILiSi {
 9     //一发现别人有动静,就要行动起来
10     public void update(StringBuffer context);
11 }

View Code

【4】李斯实现(观察者实现)

 1 package com.yeepay.sxf.template17;
 2 /**
 3  * 观察者
 4  * 李斯监控韩非子
 5  * @author sxf
 6  *
 7  */
 8 public class LiSi implements ILiSi {
 9
10     @Override
11     public void update(StringBuffer context) {
12         System.out.println("LiSi.update()李斯发现韩非子有活动");
13         System.out.println("LiSi.update()李斯行动"+context.toString());
14         System.out.println("LiSi.update()李斯回报秦始皇完毕");
15     }
16
17
18 }

View Code

【5】监视线程

 1 package com.yeepay.sxf.template17;
 2 /**
 3  * 监视线程
 4  * @author sxf
 5  *
 6  */
 7 public class Spy  extends Thread{
 8     //韩非子
 9     private HanFeiZi hanfeizi;
10     //李斯
11     private LiSi lisi;
12     //监控内容
13     private String type;
14
15     //构造函数
16     public Spy(HanFeiZi hanfeizi, LiSi lisi, String type) {
17         super();
18         this.hanfeizi = hanfeizi;
19         this.lisi = lisi;
20         this.type = type;
21     }
22
23
24     @Override
25     public void run() {
26         while(true){
27             if(this.type.equals("breakfast")){
28                 if(this.hanfeizi.isHavingBreakFast()){
29                     StringBuffer buffer=new StringBuffer().append("韩非子在吃饭");
30                     this.lisi.update(buffer);
31                     this.hanfeizi.setHavingBreakFast(false);
32                 }
33             }else if(this.type.equals("fun")){
34                 if(this.hanfeizi.isHavingFun()){
35                     StringBuffer buffer=new StringBuffer();
36                     buffer.append("韩非子在娱乐");
37                     this.lisi.update(buffer);
38                     this.hanfeizi.setHavingFun(false);
39                 }
40             }
41         }
42     }
43
44
45 }

View Code

<二>猫抓老鼠案例(符合观察者模式的标准版型)

【1】老鼠接口(被观察者自身行为接口)

 1 package com.yeepay.sxf.template17;
 2 /**
 3  * 老鼠的接口
 4  *
 5  * 被观察者自身行为的接口
 6  * @author sxf
 7  *
 8  */
 9 public interface IMouse {
10     //老鼠晚上吃饭
11     public void eatFoodByNin();
12 }

View Code

【2】观察接口(实现该接口的类都是被观察者)

 1 package com.yeepay.sxf.template17;
 2 /**
 3  *
 4  * 被观察者要实现这个接口
 5  * @author sxf
 6  *
 7  */
 8 public interface IObservable {
 9
10     //增加一个观察者
11     public void addObserver(ICat cat);
12     //删除一个观察者
13     public void deleteObserver(ICat cat);
14     //既然要观察,通知观察者
15     public void notifyObserver(String notify);
16 }

View Code

【3】猫接口(观察者接口)

 1 package com.yeepay.sxf.template17;
 2 /**
 3  * 观察者接口
 4  * 猫
 5  * @author sxf
 6  *
 7  */
 8 public interface ICat {
 9
10     public void catchMouse(String content);
11 }

View Code

【4】猫1实现(观察者实现接口)

 1 package com.yeepay.sxf.template17;
 2
 3 /**
 4  * 黑猫观察者实现
 5  * @author sxf
 6  *
 7  */
 8 public class BlackCat implements ICat{
 9
10     @Override
11     public void catchMouse(String content) {
12         System.out.println("Cat.catchMouse(黑猫抓老鼠)"+content);
13     }
14
15
16
17 }

View Code

【5】猫2实现(观察者实现接口)

 1 package com.yeepay.sxf.template17;
 2 /**
 3  * 虹猫观察者实现
 4  * @author sxf
 5  *
 6  */
 7 public class RedCat implements ICat {
 8
 9     @Override
10     public void catchMouse(String content) {
11         System.out.println("RedCat.catchMouse(虹猫抓老鼠)"+content);
12     }
13
14
15 }

View Code

【6】老鼠实现(被观察者实现接口)

 1 package com.yeepay.sxf.template17;
 2
 3 import java.util.ArrayList;
 4 /**
 5  * 老鼠,被观察者
 6  * @author sxf
 7  *
 8  */
 9 public class Mouse implements IMouse,IObservable {
10
11     //存放观察者
12     private ArrayList<ICat> cat=new ArrayList<ICat>();
13
14     @Override
15     public void addObserver(ICat cat) {
16         this.cat.add(cat);
17     }
18
19     @Override
20     public void deleteObserver(ICat cat) {
21         this.cat.remove(cat);
22     }
23
24     @Override
25     public void notifyObserver(String notify) {
26         for(ICat catd:cat){
27             catd.catchMouse(notify);
28         }
29     }
30
31     @Override
32     public void eatFoodByNin() {
33         System.out.println("Mouse.eatFoodByNin(老鼠出来吃食物)");
34         //通知观察者
35         this.notifyObserver("a老鼠出来了");
36     }
37
38
39 }

View Code

【7】客户端测试两个案例

 1 package com.yeepay.sxf.template17;
 2 /**
 3  * 客户端测试类
 4  * @author sxf
 5  *
 6  */
 7 public class ClientTest {
 8     public static void main(String[] args) {
 9         //test01();
10         test02();
11     }
12
13     /**
14      * 这个观察者模式不符合面向对象,而且把cpu搞的很疲惫
15      */
16     public static void test01(){
17         //定义出李斯,和韩非子
18         LiSi liSi=new LiSi();
19         HanFeiZi hanFeiZi=new HanFeiZi();
20         //观察早餐
21         Spy sd=new Spy(hanFeiZi, liSi, "breakfast");
22         sd.start();
23         //观察娱乐
24         Spy se=new Spy(hanFeiZi, liSi, "fun");
25         se.start();
26
27         //当前线程休息10000秒
28         try {
29             Thread.sleep(10000L);
30         } catch (InterruptedException e) {
31             // TODO Auto-generated catch block
32             e.printStackTrace();
33         }
34         //韩非子吃早饭
35         hanFeiZi.haveBreakFast();
36         try {
37             Thread.sleep(10000L);
38         } catch (InterruptedException e) {
39             // TODO Auto-generated catch block
40             e.printStackTrace();
41         }
42         //韩非子娱乐
43         hanFeiZi.haveFun();
44     }
45
46     /**
47      * 真正的观察者模式的标准版,但存在效率问题,可以用异步思想改善
48      */
49     public static void test02(){
50         //声明两个观察者
51         ICat cat1=new BlackCat();
52         ICat cat2=new RedCat();
53         //声明被观察者
54         IMouse mouse=new Mouse();
55         IObservable iObservable=(IObservable) mouse;
56         //给被观察者加入观察者
57         iObservable.addObserver(cat1);
58         iObservable.addObserver(cat2);
59         //老鼠要吃食物
60         mouse.eatFoodByNin();
61     }
62 }

View Code

 

时间: 2024-09-29 03:18:31

设计模式之禅之设计模式-观察者模式的相关文章

设计模式之禅之设计模式-桥梁模式

一:桥梁模式定义        --->桥梁模式(Bridge Pattern)也叫做桥接模式,是一个比较简单的模式        --->将抽象和实现解耦,使得两者可以独立地变化. 二:桥梁模式角色 ● Abstraction--抽象化角色        它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类.● Implementor--实现化角色        它是接口或者抽象类,定义角色必需的行为和属性.● RefinedAbstraction--修正抽象

设计模式之禅之设计模式-装饰者模式

一:装饰模式的定义        --->动态地给一个对象添加一些额外的职责.就增加功能来说,装饰模式相比生成子类更为灵活.        --->如果大家还记得代理模式,那么很容易看懂这个类图,装饰类的作用也就是一个特殊的代理类.        --->在装饰模式中,必然有一个最基本.最核心.最原始的接口或抽象类充当Component抽象构件 二:装饰模式的角色        ● Component抽象构件                Component是一个接口或者是抽象类,就是定

设计模式之禅之设计模式-策略模式

一:策略模式的定义        --->是一种比较简单的模式,也叫做政策模式        --->定义一组算法,将每个算法都封装起来,并且使它们之间可以互换 二:策略模式的三个角色 ● Context封装角色        --->它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略.算法的直接访问,封装可能存在的变化.● Strategy抽象策略角色        --->策略.算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性● ConcreteStr

设计模式之禅之设计模式-迭代器模式

一:迭代器模式的定义        --->迭代器模式(Iterator Pattern)目前已经是一个没落的模式,基本上没人会单独写一个迭代器,除非是产品性质的开发        --->它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节.        --->迭代器是为容器服务的,那什么是容器呢? 能容纳对象的所有类型都可以称之为容器,例如Collection集合类型.Set类型等,迭代器模式就是为解决遍历这些容器中的元素而诞生的        --->迭

设计模式之禅之设计模式-模板方法模式

一:模板方法模式的定义        --->定义一个操作中的算法的框架,而将一些步骤延迟到子类中.使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.        --->模板方法模式确实非常简单,仅仅使用了Java的继承机制,但它是一个应用非常广泛的模式.其中,AbstractClass叫做抽象模板,它的方法分为两类:        ● 基本方法                基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用.        ● 模板方法   

设计模式之禅之设计模式-建造者模式

一:建造者模式的定义        --->建造者模式(Builder Pattern)也叫做生成器模式,其定义如下:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示        ● Product产品类                通常是实现了模板方法模式,也就是有模板方法和基本方法,这个参考模板方法模式.例子中的BenzModel和BMWModel就属于产品类.        ● Builder抽象建造者                规范产品的组建,一般是由子类

设计模式之禅之设计模式-责任链模式

一:责任链模式的定义        --->使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止.        --->责任链模式的重点是在"链"上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果        --->一般会有一个封装类对责任模式进行封装,也就是替代Client类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了

设计模式之禅之设计模式-访问者模式

一:访问者模式定义        --->封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作. 二:访问者模式角色● Visitor--抽象访问者        抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的.● ConcreteVisitor--具体访问者        它影响访问者访问到一个类后该怎么干,要做什么事情.● Element--抽象元素        接口或者抽象类,声

设计模式之禅之设计模式-享元模式

一:享元模式定义        --->享元模式(Flyweight Pattern)是池技术的重要实现方式        --->使用共享对象可有效地支持大量的细粒度的对象        --->要求细粒度对象,那么不可避免地使得对象数量多且性质相近,那我们就将些对象的信息分为两个部分:内部状态(intrinsic)与外部状态(extrinsic).                ● 内部状态                        内部状态是对象可共享出来的信息,存储在享元对象