设计模式--观察者(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
原文地址:https://www.cnblogs.com/blacksonny/p/4609374.html