[关闭]
@w1992wishes 2017-11-01T10:13:42.000000Z 字数 7009 阅读 907

设计模式--观察者模式

设计模式 行为型模式


目录

本文的结构如下:

一、什么是观察者模式

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

上面是比较官方的定义,可以用订阅杂志的模式来通俗理解观察者模式。

如上图,现有一本文学杂志,一一、二二、三三都喜欢文学,所以花钱订阅了该杂志,四四不喜欢文学,在其他三人订阅杂志的时候,他选择躲在窝里睡大觉。

新的一月到来,杂志发布了最新的一期,这天一大早,杂志社就把杂志分别送到了一一、二二、三三手中,三人坐着椅子上,一边喝着热牛奶,一边翻看着绝世好文章,四四只能落寞躲在角落上,偷偷打起农药。

这就是观察者模式的通俗解释,一个“主题”,有一个或多个“观察者”,“观察者”订阅“主题”,“主题”更新,订阅了该“主题”的“观察者”都能收到通知,并进行相应的行为。

二、为什么要用观察者模式

软件系统常常要求建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,但是同时需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。

三、观察者模式的结构

这是摘自《HeadFirst 设计模式》一书中的类图。从图中应该能很清楚看出观察者模式涉及的角色:

四、代码示例

用代码实现“订阅报纸”,观察者模式运用如下:

1.首先有抽象主题

  1. /**
  2. * 抽象主题
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public interface Subject {
  7. void registerObserver(Observer observer);
  8. void removeObserver(Observer observer);
  9. void notifyObservers();
  10. }

2.有实现抽象主题的具体主题

  1. /**
  2. * 具体主题
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public class Maganize implements Subject {
  7. private List<Observer> observers;
  8. private String flag;
  9. public Maganize(){
  10. observers = new ArrayList<Observer>();
  11. }
  12. public void registerObserver(Observer observer) {
  13. observers.add(observer);
  14. }
  15. public void removeObserver(Observer observer) {
  16. int i = observers.indexOf(observer);
  17. if (i > 0){
  18. observers.remove(i);
  19. }
  20. }
  21. public void notifyObservers() {
  22. for (int i=0; i<observers.size(); i++){
  23. Observer observer = (Observer)observers.get(i);
  24. observer.update(flag);
  25. }
  26. }
  27. public void publishNewMaganize(String flag){
  28. this.flag = flag;
  29. System.out.println("publish new maganize: " + flag);
  30. notifyObservers();
  31. }
  32. }

3.还有抽象观察者

  1. /**
  2. * 抽象观察者
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public interface Observer {
  7. void update(String flag);
  8. }

4.还有实现抽象观察者的三个具体观察者

  1. /**
  2. * 具体观察者一一
  3. * Created by w1992wishes on 2017/10/17.
  4. */
  5. public class Yiyi implements Observer {
  6. private String flag;
  7. public void update(String flag) {
  8. this.flag = flag;
  9. System.out.println("I am yiyi, now reading maganize: " + flag);
  10. }
  11. }
  1. /**
  2. * 具体观察者--二二
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public class Erer implements Observer {
  7. private String flag;
  8. public void update(String flag) {
  9. this.flag = flag;
  10. System.out.println("I am erer, now reading maganize: " + flag);
  11. }
  12. }
  1. /**
  2. * 具体观察者三三
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public class Sansan implements Observer {
  7. private String flag;
  8. public void update(String flag) {
  9. this.flag = flag;
  10. System.out.println("I am sansan, now reading maganize: " + flag);
  11. }
  12. }

5.最后有客户端

  1. /**
  2. * 客户端
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public class Client {
  7. public static void main(String[] args) {
  8. //创建主题对象
  9. Maganize maganize = new Maganize();
  10. //创建观察者
  11. Observer yiyi = new Yiyi();
  12. Observer erer = new Erer();
  13. Observer sansan = new Sansan();
  14. //将观察者对象登记到主题对象上
  15. maganize.registerObserver(yiyi);
  16. maganize.registerObserver(erer);
  17. maganize.registerObserver(sansan);
  18. //改变主题对象的状态(发布新杂志)
  19. maganize.publishNewMaganize("October New");
  20. }
  21. }

显示结果如下:
publish new maganize: October New
I am yiyi, now reading maganize: October New
I am erer, now reading maganize: October New
I am sansan, now reading maganize: October New

在运行时,这个客户端首先创建了具体主题类的实例,以及三个观察者对象。然后,它调用主题对象的registerObserver()方法,将观察者对象向主题对象登记,也就是将它加入到主题对象的聚集中去。

这时,客户端调用主题的publishNewMaganize()方法,改变了主题对象的内部状态。主题对象在状态发生变化时,调用超类的notifyObservers()方法,通知所有登记过的观察者对象。

五、观察者模式的推模型和拉模型

6.1、推模型和拉模型

在观察者模式中,又分为推模型和拉模型两种方式。

主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。

主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

根据上面的描述,发现前面的例子就是典型的推模型,下面给出一个拉模型的实例。

6.2、拉模型

1.拉模型通常都是把主题对象当做参数传递。

  1. /**
  2. * 拉模型 抽象观察者
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public interface Observer {
  7. /**
  8. * update 方法
  9. *
  10. * @param subject 传入主题对象,方面获取相应的主题对象的状态
  11. */
  12. void update(Subject subject);
  13. }

2.拉模型的具体观察者类

  1. /**
  2. * 拉模型 具体观察者一一
  3. * Created by w1992wishes on 2017/10/17.
  4. */
  5. public class Yiyi implements Observer {
  6. private String flag;
  7. public void update(Subject subject) {
  8. this.flag = ((Maganize) subject).getFlag();
  9. System.out.println("I am yiyi, now reading maganize: " + flag);
  10. }
  11. }
  1. /**
  2. * 拉模型 具体观察者--二二
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public class Erer implements Observer {
  7. private String flag;
  8. public void update(Subject subject) {
  9. this.flag = ((Maganize) subject).getFlag();
  10. System.out.println("I am erer, now reading maganize: " + flag);
  11. }
  12. }
  1. /**
  2. * 拉模型 具体观察者三三
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public class Sansan implements Observer {
  7. private String flag;
  8. public void update(Subject subject) {
  9. this.flag = ((Maganize) subject).getFlag();
  10. System.out.println("I am sansan, now reading maganize: " + flag);
  11. }
  12. }

3.拉模型 拉模型的具体主题类

拉模型的主题类主要的改变是nodifyObservers()方法。在循环通知观察者的时候,也就是循环调用观察者的update()方法的时候,传入的参数不同了。

  1. /**
  2. * 具体主题
  3. *
  4. * Created by w1992wishes on 2017/10/17.
  5. */
  6. public class Maganize implements Subject {
  7. private List<Observer> observers;
  8. private String flag;
  9. public Maganize(){
  10. observers = new ArrayList<Observer>();
  11. }
  12. public void registerObserver(Observer observer) {
  13. observers.add(observer);
  14. }
  15. public void removeObserver(Observer observer) {
  16. int i = observers.indexOf(observer);
  17. if (i > 0){
  18. observers.remove(i);
  19. }
  20. }
  21. public void notifyObservers() {
  22. for (int i=0; i<observers.size(); i++){
  23. Observer observer = (Observer)observers.get(i);
  24. observer.update(this);
  25. }
  26. }
  27. public void publishNewMaganize(String flag){
  28. this.flag = flag;
  29. System.out.println("publish new maganize: " + flag);
  30. notifyObservers();
  31. }
  32. public String getFlag() {
  33. return flag;
  34. }
  35. }

6.3、两种模式的比较

六、观察者模式的优点和缺点

6.1、观察者模式的优点

6.2、观察者模式的缺点

七、观察者模式的适用环境

在以下情况下可以使用观察者模式:

八、观察者模式应用举例

观察者模式在软件开发中应用非常广泛,如某电子商务网站可以在执行发送操作后给用户多个发送商品打折信息,某团队战斗游戏中某队友牺牲将给所有成员提示等等,凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式。

九、总结

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注