观察者模式是一种常用的行为型设计模式,它用于实现对象之间的松耦合通信。通过观察者模式,一个对象(称为主题)可以将消息通知给一组依赖它的对象(称为观察者),从而实现对象间的消息传递和状态更新。本文将详细介绍观察者模式的原理、结构和使用方法,并通过详细的 Java 示例代码来说明。
1. 观察者模式的定义
观察者模式是一种对象间的通信方式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,它会通知所有的观察者对象,使它们能够自动更新自己。观察者模式包含三个核心角色:主题(Subject)、观察者(Observer)和具体观察者(Concrete Observer)。
- 主题(Subject):主题是被观察的对象,它包含一组观察者对象,并提供注册、注销和通知观察者的方法。主题通常维护一个状态,当状态发生变化时,会通知所有观察者。
- 观察者(Observer):观察者是依赖于主题的对象,它定义了一个更新接口,用于接收主题的通知。观察者可以注册到主题中,当主题状态发生变化时,观察者会被通知并执行相应的操作。
- 具体观察者(Concrete Observer):具体观察者是观察者的具体实现,它实现了更新接口,以便接收主题的通知,并根据通知执行相应的操作。
观察者模式的核心思想是主题和观察者之间的解耦,主题不需要知道观察者的具体实现,只需知道观察者实现了更新接口。这样,主题和观察者可以独立演化,互不影响。
2. 观察者模式的结构
观察者模式包含以下几个核心角色:
- 主题(Subject):主题是被观察的对象,它包含一组观察者对象,并提供注册、注销和通知观察者的方法。
- 观察者(Observer):观察者是依赖于主题的对象,它定义了一个更新接口,用于接收主题的通知。
- 具体观察者(Concrete Observer):具体观察者是观察者的具体实现,它实现了更新接口,以便接收主题的通知,并根据通知执行相应的操作。
下图展示了观察者模式的结构:
3. 观察者模式的工作原理
观察者模式的工作原理可以简述如下:
- 定义主题接口,其中包含注册、注销和通知观察者的方法。
- 定义观察者接口,其中包含更新方法,用于接收主题的通知。
- 定义具体主题类,实现主题接口。具体主题维护观察者列表,并在状态发生变化时通知观察者。
- 定义具体观察者类,实现观察者接口。具体观察者注册到具体主题中,并在接收到通知时执行相应的操作。
以下是一个简单的 Java 示例代码,演示了观察者模式的使用。
首先,我们定义主题接口 Subject
,其中包含注册、注销和通知观察者的方法:
public interface Subject {
void registerObserver(Observer observer);
void unregisterObserver(Observer observer);
void notifyObservers();
}
然后,我们定义观察者接口 Observer
,其中包含更新方法:
public interface Observer {
void update();
}
接下来,我们定义具体主题类 ConcreteSubject
,实现主题接口。具体主题维护一个观察者列表,并在状态发生变化时通知观察者:
import java.util.ArrayList;
import java.util.List;
public class ConcreteSubject implements Subject {
private List<Observer> observers;
private int state;
public ConcreteSubject() {
observers = new ArrayList<>();
}
public void setState(int state) {
this.state = state;
notifyObservers();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void unregisterObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
最后,我们定义具体观察者类 ConcreteObserver
,实现观察者接口。具体观察者注册到具体主题中,并在接收到通知时执行相应的操作:
public class ConcreteObserver implements Observer {
private String name;
private ConcreteSubject subject;
public ConcreteObserver(String name, ConcreteSubject subject) {
this.name = name;
this.subject = subject;
subject.registerObserver(this);
}
@Override
public void update() {
System.out.println(name + " received notification. New state: " + subject.getState());
}
}
在客户端中,我们创建具体主题对象和具体观察者对象,并进行通信:
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver("Observer 1", subject);
ConcreteObserver observer2 = new ConcreteObserver("Observer 2", subject);
subject.setState(1);
subject.setState(2);
subject.unregisterObserver(observer2);
subject.setState(3);
}
}
输出结果为:
Observer 1 received notification. New state: 1
Observer 2 received notification. New state: 1
Observer 1 received notification. New state: 2
Observer 2 received notification. New state: 2
Observer 1 received notification. New state: 3
从输出结果可以看出,通过观察者模式,具体主题对象将状态变化通知给所有的具体观察者对象,观察者对象接收到通知后执行相应的操作。
4. 观察者模式的优点和适用场景
观察者模式具有以下优点:
- 松耦合:观察者模式实现了主题和观察者之间的松耦合,主题不需要知道观察者的具体实现,只需知道观察者实现了更新接口。这样,主题和观察者可以独立演化,互不影响。
- 扩展性:通过添加新的观察者,可以方便地扩展主题的功能,而无需修改主题的代码。
- 可复用性:主题和观察者可以在不同的场景中进行复用,从而提高代码的可复用性。
观察者模式适用于以下场景:
- 当一个对象的改变需要同时改变其他对象,并且它不知道具体有多少对象需要改变时,可以考虑使用观察者模式。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,而这两个方面又需要独立地改变和复用时,可以考虑使用观察者模式。
- 当一个对象的状态改变需要通知其他对象,但是这些对象的数量和类型在运行时可以动态地变化时,可以考虑使用观察者模式。
观察者模式在实际开发中有广泛的应用,例如:
- GUI 编程中,窗口和控件之间的交互可以使用观察者模式来实现。
- 订阅-发布模型中,发布者和订阅者之间的通信可以使用观察者模式来实现。
- 消息队列系统中,生产者和消费者之间的通信可以使用观察者模式来实现。
- 股票市场中,股票价格变化时会通知相关的观察者。
需要根据具体的业务场景和需求来判断是否适合使用观察者模式。观察者模式能够实现对象间的松耦合通信,提高代码的灵活性和可维护性,但也需要注意控制观察者的数量和处理好性能问题。
公众号请关注"果酱桑", 一起学习,一起进步!