关于网站规划建设方案书夫唯seo
十七、Observer模式:发送状态变化通知
Observer :“进行观察的人”,也就是“观察者”。
在 Observer模式中,当观察对象的状态发生变化时,会通知给观察者。
适用场景:根据对象状态进行相应处理.
示例程序:观察者将观察一个会生成数值的对象,并将它生成的数值结果显示出来。不同的观察者的显示方式不一样:DigitObserver以数字形式显示数值,GraphObserver以简单的图示形式显示数值。
示例程序类图
Observer
public interface Observer {public abstract void update(NumberGenerator generator);
}
NumberGenerator
import java.util.ArrayList;
import java.util.Iterator;public abstract class NumberGenerator {private ArrayList observers = new ArrayList(); // 保存Observer们public void addObserver(Observer observer) { // 注册Observerobservers.add(observer);}public void deleteObserver(Observer observer) { // 删除Observerobservers.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
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
public class DigitObserver implements Observer {// 接收参数为NumberGenerator的实例,然后通过调用NumberGenerator类的实例的getNumber方法可以获取到当前的数值,并将这个数值显示出来。public void update(NumberGenerator generator) {System.out.println("DigitObserver:" + generator.getNumber());try {// 为能够看清它是如何显示数值的,使用Thread.sleep来降低程序的运行速度。Thread.sleep(100);} catch (InterruptedException e) {}}
}
GraphObserver
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();}
}
角色
- Subject(观察对象)
定义了注册观察者和删除观察者的方法。
声明了“获取现在的状态”的方法。
示例中是NumberGenerator类。 - ConcreteSubject(具体的观察对象)
当自身状态发生变化后,它会通知所有已经注册的Observer角色。
示例中是RandomNumberGenerator类。 - Observer(观察者)
接收来自Subject角色的状态变化的通知。为此,它声明了update方法。
示例中是Observer接口。 - ConcreteObserver (具体的观察者)
当它的update方法被调用后,会去获取要观察的对象的最新状态。
示例中是DigitObserver类和GraphObserver类。
拓展思路的要点
这里也出现了可替换性
使用设计模式的目的之一就是使类成为可复用的组件。
在本模式中,有带状态的ConcreteSubject角色和接收状态变化通知的ConcreteObserver角色。
连接这两个角色的就是它们的接口(API) Subject角色和Observer角色。
一方面RandomNumberGenerator类并不知道,也无需在意正在观察自己的(自己需要通知的对象)到底是DigitObserver类的实例还是GraphObserver类的实例。不过它知道在它的observers字段中所保存的观察者们都实现了Observer接口。因为这些实例都是通过addObserver方法注册的,这就确保了它们一定都实现了Observer接口,一定可以调用它们的update方法。
另一方面,DigitObserver类也无需在意自己正在观察的究竟是RandomNumberGenerator类的实例还是其他XXXXNumberGenerator类的实例。不过,DigitObserver类知道它们是NumberGenerator类的子类的实例,并持有getNumber方法。
利用抽象类和接口从具体类中抽出抽象方法
在将实例作为参数传递至类中,或者在类的字段中保存实例时,不使用具体类型,而是使用抽象类型和接口
这样的实现方式可以轻松替换具体类。
Observer的顺序
在示例的notifyObservers方法中,先注册的Observer的update方法会先被调用。
通常在设计时就要注意调用顺序。
在示例中,绝不能因为先调用 DigitObserver的update方法后调用 GraphObserver的update方法而导致应用程序不能正常工作。
通常,只要保持各个类的独立性,就不会有上面这种类的依赖关系混乱问题。
不过,还需要注意下面将要提到的情况。
当Observer的行为会对Subject产生影响时
在示例中,RandomNumberGenerator类会在自身内部生成数值,调用 update方法。
但在通常的 Observer模式中,也可能是其他类触发Subject角色调用update方法。
例如,在GUI应用程序中,多数情况下是用户按下按钮后会触发update方法被调用。
Observer角色也有可能会触发 Subject 角色调用 update方法。这时注意不要导致方法被循环调用。
Subject 状态发生变化
↓
通知Observer
↓
Observer调用 Subject 的方法
↓
导致Subject状态发生变化
↓
通知Observer
↓
……
传递更新信息的方式
传递给u