转自:$Java设计模式之——观察者模式(Observer)

 

  (一)观察者模式简介

  1、定义:定义对象间一种一对多的依赖关系,一个对象状态发生改变时,所有依赖它的对象都会接到通知并作出相应的响应。

  2、应用场景:

  (1)GUI系统

  (2)订阅-发布系统

  (3)事件多级触发场景

  (4)当一个对象改变时需要通知其他对象,但不知道有其他对象具体有哪些时

  3、UML类图

  

  (二)观察者模式实例

  1、假设有个珠宝公司要运送一批钻石,强盗也盯上这批钻石了,准备在运输途中抢劫,而珠宝公司雇佣的保镖要全程对钻石进行保护,警察也派出警车护航,关系如下图:

  2、代码如下:

  (1)抽象观察者接口:

复制代码
1 /**
2  * 抽象观察者
3  *
4  */
5 
6 public interface Watcher {
7     // 对被观察者状态变化做出响应的抽象方法
8     public void update(String msg);
9 }
复制代码

  (2)抽象被观察者接口:

复制代码
 1 /**
 2  * 抽象被观察者
 3  *
 4  */
 5 
 6 public interface Watched {
 7     // 添加观察者
 8     public void addWatcher(Watcher watcher);
 9 
10     // 移除观察者
11     public void removeWatcher(Watcher watcher);
12 
13     // 通知观察者
14     public void notifyWatchers(String msg);
15 }
复制代码

  (3)保镖类:

复制代码
 1 /**
 2  * 保镖类,实现Watcher接口
 3  *
 4  */
 5 
 6 public class Security implements Watcher {
 7 
 8     @Override
 9     public void update(String msg) {
10         System.out.println("保镖收到消息:" + msg + "。保镖开始保护!");
11     }
12 
13 }
复制代码

  (4)警察类:

复制代码
 1 /**
 2  * 警察类,实现Watcher接口
 3  *
 4  */
 5 
 6 public class Police implements Watcher {
 7 
 8     @Override
 9     public void update(String msg) {
10         System.out.println("警察收到消息:" + msg + "。警察开始派警车护航!");
11     }
12 
13 }
复制代码

  (5)强盗类:

复制代码
/**
 * 强盗类,实现Watcher接口
 *
 */

public class Thief implements Watcher {

    @Override
    public void update(String msg) {
        System.out.println("收到消息:" + msg + "。强盗准备动手!");
    }

}
复制代码

  (6)珠宝运输类:

复制代码
 1 /**
 2  * 具体的被观察者
 3  *
 4  */
 5 
 6 public class Transporter implements Watched {
 7 
 8     List<Watcher> wathcerList = new ArrayList<Watcher>();
 9 
10     @Override
11     public void addWatcher(Watcher watcher) {
12         wathcerList.add(watcher);
13     }
14 
15     @Override
16     public void removeWatcher(Watcher watcher) {
17         wathcerList.remove(watcher);
18     }
19 
20     @Override
21     public void notifyWatchers(String msg) {
22         for (Watcher w : wathcerList) {
23             w.update(msg);
24         }
25     }
26 
27 }
复制代码

  (6)测试类:

复制代码
 1 public class Test {
 2     public static void main(String[] args) {
 3         Security s = new Security();
 4         Thief t = new Thief();
 5         Police p = new Police();
 6 
 7         Transporter transPorter = new Transporter();
 8         transPorter.addWatcher(s);
 9         transPorter.addWatcher(t);
10         transPorter.addWatcher(p);
11 
12         transPorter.notifyWatchers("运输车队开始出发了");
13 
14         transPorter.removeWatcher(t);
15         transPorter.notifyWatchers("运输车队摆脱了强盗");
16     }
17 }
复制代码

  (7)输出结果:

保镖收到消息:运输车队开始出发了。保镖开始保护!
收到消息:运输车队开始出发了。强盗准备动手!
警察收到消息:运输车队开始出发了。警察开始派警车护航!
保镖收到消息:运输车队摆脱了强盗。保镖开始保护!
警察收到消息:运输车队摆脱了强盗。警察开始派警车护航!

以上理解起来可能有些抽象,下面再举个栗子。

下面通过一个例子来说明:
    假设如下的情况:
    AccountManager 对象能够观察 Account,这样,在帐户状态改变时,它们可以向销售人员发送一封电子邮件。
    代码如下:

public class Account {

    private int state;
     private String name;

    public String getName() {
        return name;
    }
    public Account(String name) {
        super();
        this.name = name;
    }
    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }
     @Override
    public String toString() {
        return name;
    }
}

public class AccountManager {

    public void sendEmail(Account account) {
         System.out.println("send Email:" + account);
    }
}

 

来看一下java代码是如何实现观察者模式的


Java 语言的观察者
虽然实现的差异很明显,但在它们之间还是有一些相似之处。不论如何实现观察者,代码中都必须回答以下 4 个问题:
   1. 哪个对象是主体,哪个对象是观察者?
   2. 什么时候主体应当向它的观察者发送通知?
   3. 当接收到通知时,观察者应该做什么?
   4. 观察关系应当在什么时候开始,什么时候终止?

角色定义
    首先从标记器接口来分配角色开始。Observer 接口只定义了一个方法:update(),它对应着 Subject 发送通知时执行的操作。 Subject 承担着更多的职责。它的标记器接口定义了两个方法,一个用来跟踪观察者,另一个用来通知事件的那些观察者。

public interface Observer {

    public void update(Subject subject);
}

public interface Subject {   
    public void addObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}  

 

一旦定义了这些角色,就可以把它们应用到系统中对应的角色上。


应用观察者角色

public class AccountManager implements Observer {

   public void update(Subject subject) {
        sendEmail((Account) subject);
    }
}

 

跟踪和通知观察者

一旦这项工作完成,就可以转移到Subject。在这里,要对 Account进行修改:

 private Set observers = new HashSet();  
  public void addObserver(Observer o) {
    observers.add(o);
  }
  public void removeObserver(Observer o) {
    observers.remove(o);
  }
  public void notifyObservers() {
    for (Observer o : observers) {
      o.update(this);
    }
  }

触发事件
    现在已经把类调整到它们在模式中的角色上了。但是,还需要回过头来,在对应的事件发生时触发通知。

    Account类
    public void setState(int state) {
        if (this.state != state) {
            this.state = state;
            notifyObservers();
        }
    }

启动观察关系

public class ObserverClient {

    public static void main(String[] args) {
        AccountManager manager = new AccountManager();
        AccountManager manager2 = new AccountManager();
        Account account = new Account("Account1");
        account.addObserver(manager);
        account.addObserver(manager2);
        account.setState(1);
    }
}

 

posted on 2018-07-16 16:03  灯火、阑珊处  阅读(212)  评论(0编辑  收藏  举报