简说设计模式——观察者模式

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

简说设计模式——观察者模式
⼀、什么是观察者模式
观察者⼀般可以看做是第三者,⽐如在学校上⾃习的时候,⼤家肯定都有过交头接⽿、各种玩耍的经历,这时总会有⼀个“放风”的⼩伙伴,当⽼师即将出现时及时“通知”⼤家⽼师来了。

再⽐如,拍卖会的时候,⼤家相互叫价,拍卖师会观察最⾼标价,然后通知给其它竞价者竞价,这就是⼀个观察者模式。

对于观察者模式⽽⾔,肯定有观察者和被观察者之分。

⽐如在⼀个⽬录下建⽴⼀个⽂件,这时系统会通知⽬录管理器增加⽬录,并通知磁盘减少空间,在这⾥,⽂件就是观察者,⽬录管理器和磁盘就是被观察者。

观察者模式(Observer ),⼜叫发布-订阅模式(Publish/Subscribe ),定义对象间⼀种⼀对多的依赖关系,使得每当⼀个对象改变状态,则所有依赖于它的对象都会得到通知并⾃动更新。

UML
结构图如下:
其中,Subject 类是主题,它把所有对观察者对象的引⽤⽂件存在了⼀个聚集⾥,每个主题都可以有任何数量的观察者。

抽象主题提供了⼀个接⼝,可以增加和删除观察者对象;Observer 类是抽象观察者,为所有的具体观察者定义⼀个接⼝,在得到主题的通知时更新⾃⼰;ConcreteSubject 类是具体主题,将有关状态存⼊具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知;ConcreteObserver 是具体观察者,实现抽象观察者⾓⾊所要求的更新接⼝,以便使本⾝的状态与主题的状态相协同。

1. 主题Subject
⾸先定义⼀个观察者数组,并实现增、删及通知操作。

它的职责很简单,就是定义谁能观察,谁不能观察,⽤Vector 是线程同步的,⽐较安全,也可以使⽤ArrayList ,是线程异步的,但不安全。

2. 抽象观察者Observer
观察者⼀般是⼀个接⼝,每⼀个实现该接⼝的实现类都是具体观察者。

1 public class Subject {
2
3 //观察者数组
4 private Vector<Observer> oVector = new Vector<>();
5
6 //增加⼀个观察者
7 public void addObserver(Observer observer) {
8 this .oVector.add(observer);
9 }
10
11 //删除⼀个观察者
12 public void deleteObserver(Observer observer) {
13 this .oVector.remove(observer);
14 }
15
16 //通知所有观察者
17 public void notifyObserver() {
18 for (Observer observer : this .oVector) {
19 observer.update();
20 }
21 }
22
23 }
3. 具体主题
继承Subject 类,在这⾥实现具体业务,在具体项⽬中,该类会有很多变种。

4. 具体观察者
实现Observer 接⼝。

5. Client 客户端
⾸先创建⼀个被观察者,然后定义⼀个观察者,将该被观察者添加到该观察者的观察者数组中,进⾏测试。

运⾏结果如下:
⼆、观察者模式的应⽤
1. 何时使⽤
⼀个对象状态改变,所有的依赖对象都将得到通知
2. ⽅法1 public interface Observer {
2 //更新
3 public void update();
4 }1 public class ConcreteSubject extends Subject {
2
3 //具体业务
4 public void doSomething() {
5 //...
6 super .notifyObserver();
7 }
8
9 }
1 public class ConcreteObserver implements Observer {
2
3 @Override
4 public void update() {
5 System.out.println("收到消息,进⾏处理");
6 }
7
8 } 1 public class Client {
2
3 public static void main(String[] args) {
4 //创建⼀个主题
5 ConcreteSubject subject = new ConcreteSubject();
6 //定义⼀个观察者
7 Observer observer = new ConcreteObserver();
8 //观察
9 subject.addObserver(observer);
10 //开始活动
11 subject.doSomething();
12 }
13
14 }
使⽤⾯向对象技术
3. 优点
观察者和被观察者是抽象耦合的
建⽴了⼀套触发机制
4. 缺点
如果⼀个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
如果观察者和观察⽬标间有循环依赖,可能导致系统崩溃
没有相应的机制让观察者知道所观察的⽬标对象是怎么发⽣变化的
5. 使⽤场景
关联⾏为场景
事件多级触发场景
跨系统的消息变换场景,如消息队列的处理机制
6. 应⽤实例
⼿机丢了,委托别⼈给其他⼈发消息通知
通知⽼师/⽼板来了
拍卖,拍卖师观察最⾼标价,然后通知给其它竞价者竞价
在⼀个⽬录下建⽴⼀个⽂件,会同时通知⽬录管理器增加⽬录,并通知磁盘减少空间,⽂件是被观察者,⽬录管理器和磁盘管理器是观察者
猫叫了⼀声,吓着了⽼⿏,也惊到了主⼈,猫是被观察者,⽼⿏和⼈是观察者
7. 注意事项
避免循环引⽤
如果顺序执⾏,某⼀观察者错误会导致系统卡壳,⼀般采⽤异步⽅式
三、观察者模式的实现
下⾯举⼀个具体实例,假设上班时间有⼀部分同事在看股票,⼀部分同事在看NBA,这时⽼板回来了,前台通知了部分同事⽼板回来了,这些同事及时关闭了⽹页没被发现,⽽没被通知到的同事被抓了个现⾏,被⽼板亲⾃“通知”关闭⽹页,UML图如下:
1. 通知者接⼝
1public interface Subject {
2
3//增加
4public void attach(Observer observer);
5//删除
6public void detach(Observer observer);
7//通知
8public void notifyObservers();
9
10//状态
11public void setAction(String action);
12public String getAction();
13
14 }
2. 观察者
1public abstract class Observer {
2
3protected String name;
4protected Subject subject;
5
6public Observer(String name, Subject subject) {
= name;
8this.subject = subject;
9 }
10
11public abstract void update();
12
13 }
3. 具体通知者
前台Secretary和⽼板Boss作为具体通知者,实现Subject接⼝。

这⾥只给出Secretary类的代码,Boss类与之类似。

1public class Secretary implements Subject {
2
3//同事列表
4private List<Observer> observers = new LinkedList<>();
5private String action;
6
7//添加
8 @Override
9public void attach(Observer observer) {
10 observers.add(observer);
11 }
12
13//删除
14 @Override
15public void detach(Observer observer) {
16 observers.remove(observer);
17 }
18
19//通知
20 @Override
21public void notifyObservers() {
22for(Observer observer : observers) {
23 observer.update();
24 }
25 }
26
27//前台状态
28 @Override
29public String getAction() {
30return action;
31 }
32
33 @Override
34public void setAction(String action) {
35this.action = action;
36 }
37
38 }
4. 具体观察者
StockObserver是看股票的同事,NBAObserver是看NBA的同事,作为具体观察者,继承Observer类。

这⾥只给出StockObserver类的代码,NBAObserver类与之类似。

1public class StockObserver extends Observer {
2
3public StockObserver(String name, Subject subject) {
5. 前台作为通知者进⾏通知(Client )
前台作为通知者,通知观察者。

这⾥添加adam 和tom 到通知列表,并从通知列表中删除了adam ,测试没在通知列表中的对象不会收到通知。

运⾏结果如下,只有tom 接收到了通知:
6. ⽼板作为通知者进⾏通知(Client )
⽼板作为通知者,通知观察者。

这⾥将tom 从⽼板的通知列表中移除,⽼板只通知到了adam 。

4 super (name, subject);
5 }
6
7 @Override
8 public void update() {
9 System.out.println(subject.getAction() + "\n" + name + "关闭股票⾏情,继续⼯作");
10 }
11
12 } 1 public class Client {
2
3 public static void main(String[] args) {
4 //前台为通知者
5 Secretary secretary = new Secretary();
6
7 StockObserver observer = new StockObserver("adam", secretary);
8 NBAObserver observer2 = new NBAObserver("tom", secretary);
9
10 //前台通知
11 secretary.attach(observer);
12 secretary.attach(observer2);
13
14 //adam 没被前台通知到,所以被⽼板抓了个现⾏
15 secretary.detach(observer);
16
17 //⽼板回来了
18 secretary.setAction("⼩⼼!Boss 回来了!");
19 //发通知
20 secretary.notifyObservers();
21 }
22
23 } 1 public class Client {
2
3 public static void main(String[] args) {
4 //⽼板为通知者
5 Boss boss = new Boss();
6
7 StockObserver observer = new StockObserver("adam", boss);
8 NBAObserver observer2 = new NBAObserver("tom", boss);
9
10 //⽼板通知
11 boss.attach(observer);
12 boss.attach(observer2);
13
14 //tom 没被⽼板通知到,所以不⽤挨骂
15 boss.detach(observer2);
16
17 //⽼板回来了
18 boss.setAction("咳咳,我⼤Boss 回来了!");
19 //发通知
20 boss.notifyObservers();
21 }
22
23 }
运⾏结果如下,只有adam挨骂了: 
当⼀个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象有待改变的时候,应该考虑使⽤观察者模式。

⽽使⽤观察者模式的动机在于:将⼀个系统分割成⼀系列相互协作的类有⼀个很不好的副作⽤,就是需要维护相关对象间的⼀致性,我们不希望为了维持⼀致性⽽使各类紧密耦合,这样会给维护、扩展和重⽤都带来不便,⽽观察者模式所做的⼯作就是在解除耦合。

源码地址:。

相关文档
最新文档