在Java中使用观察者模式

首先

我会总结一下我读过的《学习Java语言的设计模式入门》这本书,其中介绍了GoF的设计模式。

观察者模式

「观察者」是指一个人或一个实体,负责观察、监督或记录事件、情况或对象的发展和变化。

「观察(observe)的人」被称为「观察者」。
当观察目标的状态发生变化时,会向观察者发送通知的模式称为观察者模式。
稍后会详细介绍,观察目标需要向观察者发送通知,而观察者处于被动的位置,因此也被称为发布-订阅模式。

请描述以下登场人物(汉语母语)

image.png

抽象类

    • Subject

 

    • 観察される側となるクラスです。

 

    • 自身を観察するobservers(複数のobserver)とそれらを登録・削除・通知するメソッドや自身の状態を返すメソッドを持ちます。

 

    • 先ほど述べたように、観察される側であるはずsubjectが観察する側のobserverを保持している点が注意すべき点です。

 

    • Observer

 

    • subjectを観察する側のクラスです。

 

    観察と言ってもsubjectから通知されて初めて観察対象の状態が変化したことを知ります。

实现类

    • ConcreteSubject

 

    • 具体的な観察される側となるクラスです。

 

    • 親クラスで宣言されている、自身の状態を返すgetSubjectStatusメソッドをオーバーライドします。

 

    • ConcreteObserveer

 

    • 具体的な観察する側となるクラスです。

 

    親クラスで宣言されている、観察対象の状態が変化したことを通知されるためのupdateメソッドをオーバーライドします。

具体的例子

image.png

抽象类

NumGeneratorクラス

import java.util.ArrayList;
import java.util.List;

public abstract class NumGenerator {

    // ObserverのListを保持
    private List<Observer> observers = new ArrayList<>();

    // Observerを追加
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    // Observerを削除
    public void deleteObserver(Observer observer) {
        observers.remove(observer);
    }

    // Observerへ通知
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }

    // 数を取得する
    public abstract int getNumber();

    // 数を生成する(状態の更新)
    public abstract void execute();
}

这是一个作为观察对象的Subject类。
由于可能存在多个观察者,所以它在List中保存了Observer。
此外,它还有用于注册和删除观察者的方法,以及用于通知注册者的方法。
在notifyObservers方法中,会调用具体观察者的update方法。

Observerクラス

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

这个类的作用是充当观察NumGenerator的角色。
我声明了一个用于接收通知的update方法,但实现将由继承该类的其他类来完成。

实现类

RandomNumGeneratorクラス

import java.util.Random;

public class RandomNumGenerator extends NumGenerator {
    // 乱数生成機
    private Random random = new Random();
    // 現在の数
    private int number;

    // 数を取得する
    @Override
    public int getNumber() {
        return number;
    }

    // 数を生成する(状態の更新)
    @Override
    public void execute() {
        for (int i = 0; i < 10; i++) {
            // 0~20の範囲で乱数を生成して現在の数の更新
            number = random.nextInt(21);
            System.out.println("ループカウント:" + (i + 1));
            // 数が生成されたことをobserverへ通知
            notifyObservers();
        }
    }
}

这是一个继承NumGenerator类并担当具体观察对象角色的类。
它具有表示随机数和当前数的字段,并且具有获取数值的方法和生成数值的方法。
在生成数值的方法中,它循环10次,在每个循环中获得0至20之间的随机数,并将该随机数设置为当前数值,以更新状态。
此外,它使用notifyObservers方法将更新的状态通知给每个观察者。

DigitObserverクラス

public class DigitObserver implements Observer {
    @Override
    public void update(NumGenerator generator) {
        // 観察者が持つ乱数を取得して表示
        System.out.println("DigitObserver:" + generator.getNumber());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}

在实现Observer接口的具体观察者类中,我重写了update方法。该方法用于描述当观察对象通知更新时具体观察者执行的操作。在这里,我打印出观察对象所持有的随机数(即更新后的当前数)。

StringObserverクラス

import org.apache.commons.lang3.RandomStringUtils;

public class StringObserver implements Observer {
    @Override
    public void update(NumGenerator generator) {
        System.out.print("StringObserver:");
        // 観察者が持つ乱数を引数として、ランダムなアルファベット文字列を取得して表示
        System.out.println(RandomStringUtils.randomAlphabetic(generator.getNumber()));
        System.out.println("----------------------------------------");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}

这个类充当了与DigitObserver类似的具体观察者角色。在这里,它接收作为参数的观察目标的随机数(更新后的当前数),并显示由随机字母组成的字符串。

执行类

Mainクラス

public class Main {
    public static void main(String[] args) {
        // 具体的な観察対象の生成
        NumGenerator generator = new RandomNumGenerator();
        // 具体的な観察対象に具体的な観察者を登録
        generator.addObserver(new DigitObserver());
        generator.addObserver(new StringObserver());
        // 数の生成(状態の更新)
        generator.execute();
    }
}

在进行了具体观察对象的生成和将具体观察者注册为观察对象的处理之后,通过生成数字来更新状态。

执行结果

以下是执行Main类的结果。
可以看出我已经执行了10次随机生成乱数和与乱数位数相对应的随机字符串的显示操作。
*由于使用了随机数,所以显示结果可能会有所不同。

ループカウント:1
DigitObserver:14
StringObserver:VNXxKnJCmkbOSG
----------------------------------------
ループカウント:2
DigitObserver:15
StringObserver:FUBpVQotKbSwmMX
----------------------------------------
ループカウント:3
DigitObserver:6
StringObserver:onRlXn
----------------------------------------
ループカウント:4
DigitObserver:18
StringObserver:AehtMZiGkzYgapTgok
----------------------------------------
ループカウント:5
DigitObserver:8
StringObserver:XuRUWXnb
----------------------------------------
ループカウント:6
DigitObserver:11
StringObserver:JHYAeuMfMDO
----------------------------------------
ループカウント:7
DigitObserver:12
StringObserver:sopRShHkheIO
----------------------------------------
ループカウント:8
DigitObserver:11
StringObserver:BLATKGBDccR
----------------------------------------
ループカウント:9
DigitObserver:18
StringObserver:kmSHMbZZftRyGkpaqa
----------------------------------------
ループカウント:10
DigitObserver:15
StringObserver:muYkfeGLfwYqykD
----------------------------------------

優點

通过使用观察者模式,可以将用于保持状态的类和接收状态变化通知的类分离,从而提高类的可重用性(组件化)。

运用之处

观察者模式的用途之一是在MVC模型的实现中,处理模型和视图的协作。
模型是内部数据,控制器通过检测模型(被观察对象)状态的变化,然后通知视图(观察者)来进行协作。

总结

我学习了关于通知对象状态变化的观察者模式。如果您方便,以下是示例代码供您参考。

    Observerサンプルコード

另外,关于其他设计模式,我在下面进行了总结,也请参考。

    [随時更新]Javaでデザインパターンまとめ

文献引用

    増補改訂版 Java言語で学ぶデザインパターン入門