概念
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有观察者都会收到通知并自动更新。观察者模式也被称为发布-订阅模式或者消息机制。观察者模式的核心思想是将观察者对象和主题对象解耦,使它们之间的依赖关系变得松散,从而提高程序的可维护性和可扩展性。
java 观察者(Observer)和被观察者(Observable)的具体实现
java已经实现了被观察者和观察者的接口,分别为 java.util.Observable 类和 java.util.Observer 接口来实现。
java.util.Observable 类是一个抽象类,它实现了被观察者的基本功能,包括添加、删除和通知观察者对象的方法。具体主题类可以继承 Observable 类,并在自己的业务逻辑中调用 setChanged() 方法和 notifyObservers() 方法来通知观察者对象。
java.util.Observer 接口是一个抽象接口,它定义了观察者对象的基本功能,包括更新自己的方法。具体观察者类可以实现 Observer 接口,并在 update() 方法中实现自己的业务逻辑。
Java中Observable类的具体实现
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Observable {
private boolean changed = false; // 状态是否改变
private List<Observer> observers = Collections.synchronizedList(new ArrayList<>()); // 观察者列表
// 添加观察者
public synchronized void addObserver(Observer observer) {
if (observer == null) {
throw new NullPointerException();
}
if (!observers.contains(observer)) {
observers.add(observer);
}
}
// 删除观察者
public synchronized void deleteObserver(Observer observer) {
observers.remove(observer);
}
// 清空观察者列表
public synchronized void deleteObservers() {
observers.clear();
}
// 获取观察者数量
public synchronized int countObservers() {
return observers.size();
}
// 状态是否改变
public synchronized boolean hasChanged() {
return changed;
}
// 设置状态为已改变
protected synchronized void setChanged() {
changed = true;
}
// 设置状态为未改变
protected synchronized void clearChanged() {
changed = false;
}
// 通知所有观察者更新
public void notifyObservers() {
notifyObservers(null);
}
// 通知所有观察者更新,并传递参数
public void notifyObservers(Object arg) {
List<Observer> snapshot = new ArrayList<>(observers);
for (int i = snapshot.size() - 1; i >= 0; i--) {
snapshot.get(i).update(this, arg);
}
clearChanged();
}
}
Observer接口的具体实现:
import java.util.Observable;
public interface Observer {
// 被观察者通知观察者更新的方法
void update(Observable observable, Object arg);
// 可以通过实现Observer接口,重写update方法,实现观察者更新的具体操作
}
java举例说明观察者模式
假设我们有一个在线商城,用户可以在该商城浏览商品并添加到购物车中,当用户添加商品到购物车时需要实时更新购物车中的商品数量和总价。这个功能也可以通过观察者模式来实现。
首先,我们需要定义被观察者对象—购物车。当用户添加商品到购物车时,购物车需要通知所有的观察者进行更新。
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
public class ShoppingCart extends Observable {
private List<Product> productList = new ArrayList<>();
// 添加产品到购物车
public void addProduct(Product product) {
productList.add(product);
notifyObservers(); // 通知观察者购物车中的商品数量和总价已改变
}
// 获取购物车中产品的数量
public int getProductCount() {
return productList.size();
}
// 获取购物车中所有产品的总价
public double getTotalPrice() {
double sum = 0;
for (Product p : productList) {
sum += p.getPrice();
}
return sum;
}
}
在购物车中,我们继承了Observable类,并定义了一个List对象来存储所有添加到购物车中的产品。当用户添加商品到购物车时,我们调用notifyObservers()方法来通知所有观察者购物车中的商品数量和总价已改变,这样就能及时更新界面上的数据。
接下来我们定义两个观察者对象—购物车商品数量显示器和购物车总价显示器。当购物车中的商品数量或总价发生改变时,这两个观察者需要及时更新显示的数据。
import java.util.Observable;
import java.util.Observer;
import javax.swing.JLabel;
public class ProductCountObserver implements Observer {
private JLabel label;
public ProductCountObserver(JLabel label) {
this.label = label;
}
// 更新购物车中商品数量的显示
@Override
public void update(Observable o, Object arg) {
ShoppingCart cart = (ShoppingCart) o;
label.setText("商品数量:" + cart.getProductCount());
}
}
import java.util.Observable;
import java.util.Observer;
import javax.swing.JLabel;
public class TotalPriceObserver implements Observer {
private JLabel label;
public TotalPriceObserver(JLabel label) {
this.label = label;
}
// 更新购物车中总价的显示
@Override
public void update(Observable o, Object arg) {
ShoppingCart cart = (ShoppingCart) o;
label.setText("总价:" + cart.getTotalPrice());
}
}
在这两个观察者对象中,我们实现了Observer接口,并重写了update()方法。在update()方法中,我们需要将被观察者对象强制转换为购物车对象,并根据购物车中的商品数量或总价进行显示的更新。
最后,我们在主方法中创建购物车对象、商品数量显示器、总价显示器和添加产品的按钮,并将这些组件添加到面板中,用于后续的显示。
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class OnlineShopping {
public static void main(String[] args) {
JFrame frame = new JFrame("OnlineShopping");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 建立购物车、商品数量显示器和总价显示器
ShoppingCart cart = new ShoppingCart();
JLabel productCountLabel = new JLabel("商品数量:0");
JLabel totalPriceLabel = new JLabel("总价:0.00");
// 建立添加产品按钮并注册事件监听器
JButton addButton = new JButton("添加产品");
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
double price = Math.random() * 100;
Product product = new Product("商品" + (cart.getProductCount() + 1), price);
cart.addProduct(product);
}
});
// 注册观察者并将购物车、商品数量显示器和总价显示器添加进面板中
cart.addObserver(new ProductCountObserver(productCountLabel));
cart.addObserver(new TotalPriceObserver(totalPriceLabel));
JPanel panel = new JPanel();
panel.add(productCountLabel);
panel.add(totalPriceLabel);
panel.add(addButton);
// 添加面板到窗口中并显示
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
在主方法中,我们创建了一个OnlineShopping窗口,并将购物车、商品数量显示器、总价显示器和添加产品的按钮添加进去。然后我们注册了两个观察者对象,并将购物车、商品数量显示器和总价显示器添加进面板中,用于后续的显示。
通过以上的观察者模式的实现,我们成功实现了在线商城的购物车功能,并使用了观察者模式去处理商品数量和总价的更新。
UML类图如下:
观察者模式的应用场景
① GUI 应用程序中的事件处理:在 GUI 应用程序中,用户的操作会触发各种事件,例如按钮点击、鼠标移动等。这些事件可以被视为主题对象,而 GUI 组件可以作为观察者对象来监听这些事件,从而实现事件处理的功能。
② 消息队列中的消息通知:在消息队列中,生产者会向队列中发送消息,而消费者则会从队列中接收消息。这些消息可以被视为主题对象,而消费者可以作为观察者对象来监听这些消息,从而实现消息通知的功能。
③ 网络编程中的事件处理:在网络编程中,服务器端会接收客户端的请求,并根据请求返回相应的响应。这些请求和响应可以被视为主题对象,而客户端可以作为观察者对象来监听这些请求和响应,从而实现事件处理的功能。
④ 日志记录中的事件通知:在日志记录中,程序会将各种事件记录到日志文件中,例如错误日志、调试日志等。这些事件可以被视为主题对象,而日志记录器可以作为观察者对象来监听这些事件,从而实现事件通知的功能。
⑤Spring 事件机制:Spring 框架提供了一个事件机制,可以让应用程序中的各个组件之间进行事件通知和处理。在 Spring 中,事件可以被视为主题对象,而事件监听器可以作为观察者对象来监听这些事件,从而实现事件处理的功能。
⑥ Spring AOP:Spring AOP(面向切面编程)是 Spring 框架中的一个重要组件,它可以帮助我们实现横切关注点的功能。在 Spring AOP 中,切面可以作为观察者对象来监听目标对象的方法调用,从而实现横切关注点的功能。
⑦ Spring MVC:Spring MVC 是 Spring 框架中的一个重要组件,它可以帮助我们实现 Web 应用程序的开发。在 Spring MVC 中,控制器可以作为观察者对象来监听请求的到达和响应的返回,从而实现请求处理的功能。