设计模式--观察者(Observer)

GOF给出的定义

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

定义了对象之间的一种一对多的依赖关系,这样,当一个对象的状态发生变化时,所有的依赖对象都被通知并自动更新。

包含观察者被观察者(也称为主题)

使用场景

1、有两个抽象类型相互依赖。将他们封装在各自的对象中,就可以对它们单独进行改变和复用。

2、对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。

3、一个对象必须通知其他对象,而它又不需知道其他对象是什么。

观察这模式的结构(UML)

标准实现

目标/被观察者(Subject) 在这里对观察者进行添加和删除操作,并通知观察者

复制代码
 1 /**
 2  * 被观察者对象(目标/主题对象),注册和删除观察者的接口
 3  * 
 4  * @author blacksonny
 5  * @since 2015年7月1日
 6  */
 7 public class Subject {
 8 
 9     //保存注册的观察者对象
10     private List<Observer> observers = new ArrayList<Observer>();
11     
12     //Attach observer
13     public void attach(Observer o){
14         observers.add(o);
15     }
16     
17     //Delete observer
18     public void detach(Observer o){
19         observers.remove(o);
20     }
21     
22     //Notify all observer who has register
23     protected void notifyObservers(){
24         for (Observer observer : observers) {
25             observer.update(this);
26         }
27     }
28 }
复制代码

 

观察者(Observer)  单目标对象发生变化时,通知这个对象

复制代码
 1 /**
 2  * 观察者抽象接口,当目标对象发生改变时,通知这个对象
 3  * 
 4  * @author blacksonny
 5  * @since 2015年7月1日
 6  */
 7 public interface Observer {
 8 
 9     /**
10      * 更新观察者
11      * 
12      * @param subject
13      *            被观察者对象,方便获取被观察者状态
14      */
15     void update(Subject subject);
16 }
复制代码

 

具体目标对象(ConcreteSubject)  继承Subject,在这里改变自身状态并通知观察者

复制代码
 1 /**
 2  * 具体的被观察者对象
 3  * 
 4  * @author blacksonny
 5  * @since 2015年7月1日
 6  */
 7 public class ConcreteSubject extends Subject {
 8 
 9     // 被观察者对象的状态
10     private String subjectState;
11 
12     public String getSubjectState() {
13         return subjectState;
14     }
15 
16     public void setSubjectState(String subjectState) {
17         this.subjectState = subjectState;
18         this.notifyObservers();//状态发生变化,通知观察者
19     }
20 
21 }
复制代码

 

观察者实现(ConcreteObserver) 接收被观察者(目标对象)消息,两者状态保持一致

复制代码
 1 /**
 2  * 具体的观察者对象,实现更新的方法,使自身的状态和被观察者对象状态保持一致
 3  * 
 4  * @author blacksonny
 5  * @since 2015年7月1日
 6  */
 7 public class ConcreteObserver implements Observer {
 8     // 观察者对象状态
 9     private String observerState;
10 
11     @Override
12     public void update(Subject subject) {
13         observerState = ((ConcreteSubject) subject).getSubjectState();
14 
15     }
16 
17 }
复制代码

 

JAVA自身提供的Observer模型

SalaryConreteSubject被观察者实现,继承自JAVA提供的Observable被观察者
观察者模式对消息的通知分为推、拉两种,推模式由subject发起,通知对象可以相对单一;拉模式由observer主动获取。拉模式就需要subject发送自身对象,而推模式可以是一个简单的数据类型,建议采用拉模式,易扩展。
具体到代码中如下的标注的
推方法主动传递一个消息内容 this.notifyObservers(salaryContent);
拉方法则无需传递参数,默认传递this对象 this.notifyObservers();
复制代码
 1 /**
 2  * 继承自jdk的被观察者
 3  * 
 4  * @author blacksonny
 5  * @since 2015年7月1日
 6  */
 7 public class SalaryConreteSubject extends Observable {
 8 
 9     private String salaryContent;
10 
11     public String getSalaryContent() {
12         return salaryContent;
13     }
14 
15     public void setSalaryContent(String salaryContent) {
16         this.salaryContent = salaryContent;
17         //必须调用
18         this.setChanged();
19         
20         //推模式
21         this.notifyObservers(salaryContent);
22         
23         //拉模式
24         this.notifyObservers();
25     }
26 }
复制代码
Employee具体观察者, 实现Observer接口,并实现update方法,而其中的两个参数,第一个就是拉模式的参数,第二个为退模式参数
复制代码
 1 /**
 2  * 实现java.util.Observer
 3  * 
 4  * @author blacksonny
 5  * @since 2015年7月1日
 6  */
 7 public class Employee implements Observer {
 8 
 9     @Override
10     public void update(Observable o, Object arg) {
11         System.out.println("本月发放工资情况 拉过来:" + ((SalaryConreteSubject) o).getSalaryContent());
12         System.out.println("本月发放工资情况 推过来:" + arg);
13     }
14 }
复制代码

Client测试:

复制代码
 1 /**
 2  * 测试java提供的观察者模式
 3  * 
 4  * @author blacksonny
 5  * @since 2015年7月1日
 6  */
 7 public class Client {
 8 
 9     /**
10      * @param args
11      */
12     public static void main(String[] args) {
13 
14         // 创建被观察者对象
15         SalaryConreteSubject subject = new SalaryConreteSubject();
16 
17         // 创建观察者对象
18         Employee employee = new Employee();
19         // 注册对象
20         subject.addObserver(employee);
21 
22         subject.setSalaryContent("10k");
23     }
24 }
复制代码

输出结果

本月发放工资情况 拉过来:10k
本月发放工资情况 推过来:10k

 

posted @   blacksonny  阅读(208)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示