设计模式 四、行为设计模式(1)
在设计模式的世界里,23种经典设计模式通常被分为三大类:创建型、结构型和行为型。创建型设计模式关注对象创建的问题,结构性设计模式关注于类或对象的组合和组装的问题,行为型设计模式则主要关注于类或对象之间的交互问题。
行为设计模式 的数量较多,共有11种,几乎占据了23种经典设计模式的一半。这些模式分别为:观察者模式、模板模式、策略模式、职责链模式、状态模式、迭代器模式、访问者模式、备忘录模式、命令模式、解释器模式和中介模式。
一、观察者模式
1、概述
观察者模式是一种行为设计模式,允许对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新,在这种设计模式种,发生状态改变的对象被称为“主题”(Subject),依赖它的对象成为“观察者”(Observer)。
观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。在GoF的设计模式书中,它的定义是这样的:
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
翻译成中文就是说:在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。不过,在实际的项目开发中,这两种对象的称呼是比较灵活的,有各种不同的叫法,比如:Subject-Observer 、Publisher-Subscriber、Producer-Consumer等等。不管怎么称呼,只要应用场景符合刚刚给出的定义,都可以看作观察者模式。
简单例子:假设我们有一个气象站,需要向许多不同的显示设备(如手机App、网站、电子屏幕等)提供实施天气数据。
首先我们创建一个Subject接口,表示主题:
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
接下来,创建一个Observer接口,表示观察者:
public interface Observer {
void update(float temperature, float humidity, float pressure);
}
创建一个具体的主体,如WeatherStation,实现Subject接口:
public class WeatherStation implements Subject {
private ArrayList<Observer> observers;
// 温度
private float temperature;
// 湿度
private float humidity;
// 大气压
private float pressure;
public WeatherStation() {
observers = new ArrayList<>();
}
// 注册一个观察者的方法
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
// 移除一个观察者的方法
@Override
public void removeObserver(Observer o) {
int index = observers.indexOf(o);
if (index >= 0) {
observers.remove(index);
}
}
// 通知所有的观察者
@Override
public void notifyObservers() {
// 循环所有的观察者,通知其当前的气象信息
for (Observer o : observers) {
o.update(temperature, humidity, pressure);
}
}
// 修改气象内容
public void measurementsChanged() {
notifyObservers();
}
// 当测量值发生了变化的时候
public void setMeasurements(float temperature, float humidity, float
pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
// 测量值发生了变化
measurementsChanged();
}
}
最后我们创建一个具体的观察者,如PhoneAPP,实现Observer接口:
public class PhoneApp implements Observer {
private float temperature;
private float humidity;
private float pressure;
private Subject weatherStation;
public PhoneApp(Subject weatherStation) {
this.weatherStation = weatherStation;
weatherStation.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {
System.out.println("PhoneApp: Temperature: " + temperature + "°C,
Humidity: " + humidity + "%, Pressure: " + pressure + " hPa");
}
}
现在我们可以创建一个WeatherStation实例并向其注册PhoneApp观察者。当WeatherStation的数据发生变化时,PhoneApp会收到通知并更新自己的显示。
public class Main {
public static void main(String[] args) {
WeatherStation weatherStation = new WeatherStation();
PhoneApp phoneApp = new PhoneApp(weatherStation);
// 模拟气象站数据更新
weatherStation.setMeasurements(25, 65, 1010);
weatherStation.setMeasurements(22, 58, 1005);
// 添加更多观察者 网站上显示-电子大屏
WebsiteDisplay websiteDisplay = new WebsiteDisplay(weatherStation);
ElectronicScreen electronicScreen = new ElectronicScreen(weatherStation);
// 再次模拟气象站数据更新
weatherStation.setMeasurements(18, 52, 1008);
}
}
这个例子中,我们创建了一个WeatherStation实例,并向其注册了PhoneApp、WebsiteDisplay和ElectronicScreen观察者,当WeatherStation的数据发生变化这个例子展示了观察者模式的优点:
1)观察者和主题之间解耦:主题只需要知道观察者实现了Observer接口,而无需了解具体的实现细节。
2)可以动态的添加和删除观察者:通过调用registerObserver 和 removeObserver方法,可以在运行时添加和删除观察者。
3)主题和观察者之间的通信时自动的:当主题的状态发生变化时,观察者会自动得到通知并更新自己的状态。
观察者广泛应用于各种场景,例如事件处理系统、数据同步和更新通知等。上面例子算是观察者模式的 “模板代码”,可以反应该模式大体得设计思路,在真实得软件开发中,并不需要照搬相面的模板代码。观察者模式的实现方法各式各样, 函数、类的命名等会根据业务场景的不同有很大的差别,比如 register 函数还可以 叫作 attach,remove 函数还可以叫作 detach 等等。不过,万变不离其宗,设计思 路都是差不多的。