• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

图解设计模式(17):Observer模式(发送状态变化通知)

设计模式 winrains 1年前 (2019-09-23) 47次浏览

Observer的意思是“进行观察的人“,即”观察者“的意思。
在Observer模式中,当观察对象的状态发生变化时,会通知给观察者。Observer模式适用于根据对象状态进行相应处理的场景。

1 Observer模式中的角色

  • Subject(观察对象)

表示观察对象。该角色定义了注册观察者和删除观察者的方法。它还声明了“获取现在的状态“的方法。在示例中,对应NumberGenerator类。

  • ConcreteSubject(具体的观察对象)

表示具体的被观察对象。当自身状态发生变化后,它会通知所有已经注册的Observer角色。在示例中,对应RandomNumberGenerator类。

  • Observer(观察者)

负责接收来自Subject角色的状态变化的通知。在示例中,对应Observer接口。

  • ConcreteObserver(具体的观察者)

表示具体的Observer。当它的update方法被调用后,会去获取要观察的对象的最新状态。在示例中,对应DigitObserver类和GraphObserver类。

2 Observer模式的类图

3 示例程序

观察者将观察一个会生成数值的对象,并将它生成的数值结果显示出来。不过,不同的观察者的显示方式不一样。DigitObserver会以数字形式显示数值,而GraphObserver则会以简单的图示形式显示数值。

3.1 类和接口一览表

名字 说明
Observer 表示观察者的接口
NumberGenerator 表示生成数值的对象的抽象类
RandomNumberGenerator 生成随机数的类
DigitObserver 表示以数字形式显示数值的类
GraphObserver 表示以简单的图示形式显示数值的类
Main 测试程序行为的类

3.2 类图

3.3 示例代码

Observer接口

表示“观察者“的接口,具体的观察者会实现这个接口。
用于生成数值的NumberGenerator类会调用update方法。Generator有“生成器“的意思。如果调用update方法,NumberGenerator类就会将”生成的数值发生了变化,将更新显示内容“的通知发送给Observer

public interface Observer {
    public abstract void update(NumberGenerator generator);
}

NumberGenerator类

用于生成数值的抽象类。
notifyObservers方法会向所有的observer发送通知,告诉它们“我生成的数值发生了变化,请更新显示内容“。该方法会调用每个observerupdate方法。

import java.util.ArrayList;
import java.util.Iterator;
public abstract class NumberGenerator {
    private ArrayList observers = new ArrayList();        // 保存Observer们
    public void addObserver(Observer observer) {    // 注册Observer
        observers.add(observer);
    }
    public void deleteObserver(Observer observer) { // 删除Observer
        observers.remove(observer);
    }
    public void notifyObservers() {               // 向Observer发送通知
        Iterator it = observers.iterator();
        while (it.hasNext()) {
            Observer o = (Observer)it.next();
            o.update(this);
        }
    }
    public abstract int getNumber();                // 获取数值
    public abstract void execute();                 // 生成数值
}

RandomNumberGenerator类

NumberGenerator的子类,它会生成随机数。

import java.util.Random;
public class RandomNumberGenerator extends NumberGenerator {
    private Random random = new Random();   // 随机数生成器
    private int number;                     // 当前数值
    public int getNumber() {                // 获取当前数值
        return number;
    }
    public void execute() {
        for (int i = 0; i < 20; i++) {
            number = random.nextInt(50);
            notifyObservers();
        }
    }
}

DigitObserver类

实现了Observer接口,它的功能是以数字形式显示观察到的数值。
update方法通过调用NumberGenerator类的实例的getNumber方法获取当前的数值,并将这个数值显示出来。为了能够看清它是如何显示数值的,这里使用Thread.sleep来降低程序的运行速度。

public class DigitObserver implements Observer {
    public void update(NumberGenerator generator) {
        System.out.println("DigitObserver:" + generator.getNumber());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}

GraphObserver类

实现了Observer接口,将观察到的数值以*****这样简单图示的形式显示出来。

public class GraphObserver implements Observer {
    public void update(NumberGenerator generator) {
        System.out.print("GraphObserver:");
        int count = generator.getNumber();
        for (int i = 0; i < count; i++) {
            System.out.print("*");
        }
        System.out.println("");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}

Main类

测试程序。

public class Main {
    public static void main(String[] args) {
        NumberGenerator generator = new RandomNumberGenerator();
        Observer observer1 = new DigitObserver();
        Observer observer2 = new GraphObserver();
        generator.addObserver(observer1);
        generator.addObserver(observer2);
        generator.execute();
    }
}

运行结果

DigitObserver:32
GraphObserver:********************************
DigitObserver:34
GraphObserver:**********************************
DigitObserver:0
GraphObserver:
DigitObserver:26
GraphObserver:**************************
DigitObserver:45
GraphObserver:*********************************************
DigitObserver:28
GraphObserver:****************************
DigitObserver:14
GraphObserver:**************
DigitObserver:0
GraphObserver:
DigitObserver:48
GraphObserver:************************************************
DigitObserver:7
GraphObserver:*******
DigitObserver:0
GraphObserver:
DigitObserver:43
GraphObserver:*******************************************
DigitObserver:26
GraphObserver:**************************
DigitObserver:6
GraphObserver:******
DigitObserver:43
GraphObserver:*******************************************
DigitObserver:48
GraphObserver:************************************************
DigitObserver:47
GraphObserver:***********************************************
DigitObserver:16
GraphObserver:****************
DigitObserver:30
GraphObserver:******************************
DigitObserver:43
GraphObserver:*******************************************

4 总结

4.1 可替换性

一方面,RandomNumberGenerator类并不知道,也无需在意正在观察自己的到底是DigitObserver类的实例还是GraphObserver类的实例。不过,RandomGenrator知道这些实例都实现了Observer接口,一定可以调用它们的update方法。
另一方面,DigitObserver类也无需在意自己正在观察的究竟是RandomNumberGenrator类的实例,还是其它XXXXXNumberGenerator类的实例。不过,DigitObserver类知道它们是NumberGenerator类的子类的实例,并持有getNumber方法。
可替换性的设置思想如下:

  • 利用抽象类和接口从具体类中抽出抽象方法
  • 在将实例作为参数传递至类中,或者在类的字段中保存实例时,不使用具体类型,而是使用抽象类型和接口
  • Observer的顺序

Subject角色中注册有多个Observer角色,通常在设计ConcreteObserver角色的类时,需要注意这些Observer的update方法的调用顺序,不能因为update方法的调用顺序发生改变而产生问题。

4.2 当Observer的行为会对Subject产生影响时

Observer角色也有可能会触发Subject角色调用update方法,此时,如果不注意,就可以会导致方向被循环调用。

4.3 从观察变为通知

Observer本来的意思是“观察者“,但实际上Observer角色并非主动地去观察,而是被动地接受来自Subject角色的通知。因此,Observer模式也被称为Publish-Subscribe(发布-订阅)模式。

摘自《图解设计模式》


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)