观察者模式从名字上来看大概就是一种通知与被通知的关系,其实代码思想也与其差不多,其核心思想就是有一个或N个观察者(Observer)和一个(或N个)被观察者(Observable 或 Subject),观察者以订阅方式来观察被观察者,当被观察者接到更新时(程序员控制或代码自动发出)将通知所有观察者来接受更新的内容。这就有点像一群学生(Observer,观察者)和书店老板(Observable 或 Subject,被观察者),当书店每次新进漫画杂志时,就会通知所有学生去购买。下面就看看如何用代码来实现这个事件。
观察者模式是另一种可被Lambda 表达式简化和改进的行为模式。在观察者模式中,被观察者持有一个观察者列表。当被观察者的状态发生改变,会通知观察者。观察者模式被大 量应用于基于MVC 的GUI 工具中,以此让模型状态发生变化时,自动刷新视图模块,达到二者之间的解耦。 观看GUI 模块自动刷新有点枯燥,我们要观察的对象是月球! NASA 和外星人都对登陆到月球上的东西感兴趣,都希望可以记录这些信息。NASA 希望确保阿波罗号上的航天员成功登月;外星人则希望在NASA 注意力分散之时进犯地球。
让我们先来定义观察者的API, 这里我将观察者称作LandingObserver。它只有一个 observeLanding 方法,当有东西登陆到月球上时会调用该方法
用于观察登陆到月球的组织的接口
public interface LandingObserver {
public void observeLanding(String name);
}
被观察者是月球Moon,它持有一组LandingObserver 实例,有东西着陆时会通知这些观察者,还可以增加新的LandingObserver 实例观测Moon 对象
Moon 类当然不如现实世界中那么完美
public class Moon {
private final List<LandingObserver> observers = new ArrayList<>();
public void land(String name) {
for (LandingObserver observer : observers) {
observer.observeLanding(name);
}
}
public void startSpying(LandingObserver observer) {
observers.add(observer);
}
}
我们有两个具体的类实现了LandingObserver 接口,分别代表外星人和NASA检测着陆情况。前面提到过,监测到登陆后它们有不同的反应。 外星人观察到人类登陆月球
public class Aliens implements LandingObserver {
@Override
public void observeLanding(String name) {
if (name.contains("Apollo")) {
System.out.println("They're distracted, lets invade earth!");
}
}
}
NASA 也能观察到有人登陆月球
public class Nasa implements LandingObserver {
@Override
public void observeLanding(String name) {
if (name.contains("Apollo")) {
System.out.println("We made it!");
}
}
}
和前面的模式类似,在传统的例子中,用户代码需要有一层模板类,如果使用Lambda 表达式,就不用编写这些类了
使用类的方式构建用户代码
Moon moon = new Moon();
moon.startSpying(new Nasa());
moon.startSpying(new Aliens());
moon.land("An asteroid");
moon.land("Apollo 11");
使用Lambda 表达式构建用户代码
Moon moon = new Moon();
moon.startSpying(name -> {
if (name.contains("Apollo"))
System.out.println("We made it!");
});
moon.startSpying(name -> {
if (name.contains("Apollo"))
System.out.println("They're distracted, lets invade earth!");
});
moon.land("An asteroid");
moon.land("Apollo 11");
还有一点值得思考,无论使用观察者模式或策略模式,实现时采用Lambda 表达式还是传统的类,取决于策略和观察者代码的复杂度。我这里所举的例子代码很简单,只是一两个 方法调用,很适合展示新的语言特性。然而在有些情况下,观察者本身就是一个很复杂的类,这时将很多代码塞进一个方法中会大大降低代码的可读性。
从某种角度来说,将大量代码塞进一个方法会让可读性变差是决定如何使用Lambda 表达式的黄金法则。之所以不在这里过分强调,是因为这也是编写一般方法时的黄金法则!