在软件开发中,观察者模式是一种非常常见的设计模式,用于解决对象间的依赖关系。当一个对象的状态发生改变时,需要通知其他相关对象,确保它们的状态也随之更新。本文将通过一个具体的业务场景------商品库存变化,来对比在使用和不使用观察者模式时的实现方式,从而帮助你更好地理解观察者模式的优势。
业务场景:商品库存变化通知
假设我们正在开发一个电商系统,其中涉及到多个模块对商品库存信息的依赖:
- 商品详情页:展示商品的库存数量。
- 购物车:展示用户购物车中商品的库存状态。
当商品库存数量发生变化时,我们需要通知这些模块进行同步更新。如果不使用观察者模式,可能会导致代码耦合性较强,难以扩展和维护。而使用观察者模式,则能提供一个解耦的设计,使得各个模块之间的通信更加简洁和高效。
不使用观察者模式
1. 直接调用更新方法
在没有观察者模式的情况下,当商品库存发生变化时,我们可能会直接在商品类中手动调用商品详情页和购物车的更新方法。这会导致商品类和其他模块(商品详情页、购物车)之间存在紧密的耦合。
java
// 商品类(没有观察者模式)
class Product {
private int stockQuantity; // 库存数量
private ProductDetailPage detailPage;
private ShoppingCart shoppingCart;
public Product(ProductDetailPage detailPage, ShoppingCart shoppingCart) {
this.detailPage = detailPage;
this.shoppingCart = shoppingCart;
}
// 设置库存数量并更新其他模块
public void setStockQuantity(int stockQuantity) {
this.stockQuantity = stockQuantity;
System.out.println("商品库存更新为: " + stockQuantity);
// 手动通知商品详情页和购物车更新库存
detailPage.update(stockQuantity);
shoppingCart.update(stockQuantity);
}
}
2. 商品详情页和购物车
商品详情页和购物车类会有一个 update
方法,用于接收商品库存的变化并更新显示。
java
// 商品详情页
class ProductDetailPage {
public void update(int stockQuantity) {
System.out.println("商品详情页更新库存显示为: " + stockQuantity);
}
}
// 购物车
class ShoppingCart {
public void update(int stockQuantity) {
System.out.println("购物车更新库存显示为: " + stockQuantity);
}
}
3. 测试类
在这个例子中,我们直接在商品类中管理了商品详情页和购物车的更新逻辑。
java
public class WithoutObserverPattern {
public static void main(String[] args) {
ProductDetailPage detailPage = new ProductDetailPage();
ShoppingCart shoppingCart = new ShoppingCart();
Product product = new Product(detailPage, shoppingCart);
product.setStockQuantity(10);
product.setStockQuantity(5);
}
}
运行结果
plaintext
商品库存更新为: 10
商品详情页更新库存显示为: 10
购物车更新库存显示为: 10
商品库存更新为: 5
商品详情页更新库存显示为: 5
购物车更新库存显示为: 5
存在的问题
- 耦合性强:商品类直接依赖于商品详情页和购物车,若以后新增其他依赖库存信息的模块,需要修改商品类,违反了开闭原则(OCP)。
- 扩展困难:若有多个模块依赖库存变化,每个模块都需要在商品类中手动注册,增加了维护难度。
- 难以维护:随着项目的增长,代码会变得越来越复杂,修改其中的一个部分可能会引发连锁反应,增加出错的风险。
使用观察者模式
1. 观察者接口
首先,我们定义观察者接口 Observer
,其中包含一个 update
方法,用于接收商品库存变化的通知。
java
// 定义观察者接口
interface Observer {
void update(int stockQuantity); // 更新库存数量的方法
}
2. 被观察者类(商品类)
接下来,我们定义商品类 Product
,它会维护一个观察者列表,并在库存变化时通知所有观察者。
java
import java.util.ArrayList;
import java.util.List;
// 定义被观察的商品类(Subject)
class Product {
private List<Observer> observers = new ArrayList<>(); // 观察者列表
private int stockQuantity; // 库存数量
// 添加观察者
public void addObserver(Observer observer) {
observers.add(observer);
}
// 移除观察者
public void removeObserver(Observer observer) {
observers.remove(observer);
}
// 通知所有观察者
private void notifyObservers() {
for (Observer observer : observers) {
observer.update(stockQuantity); // 更新每个观察者
}
}
// 设置库存数量,并通知观察者
public void setStockQuantity(int stockQuantity) {
this.stockQuantity = stockQuantity;
System.out.println("商品库存更新为: " + stockQuantity);
notifyObservers(); // 通知所有观察者
}
}
3. 具体观察者类(商品详情页和购物车)
商品详情页和购物车分别实现观察者接口,当商品库存变化时,它们会接收到更新通知,并更新各自的库存显示。
java
// 商品详情页(观察者)
class ProductDetailPage implements Observer {
@Override
public void update(int stockQuantity) {
System.out.println("商品详情页更新库存显示为: " + stockQuantity);
}
}
// 购物车(观察者)
class ShoppingCart implements Observer {
@Override
public void update(int stockQuantity) {
System.out.println("购物车更新库存显示为: " + stockQuantity);
}
}
4. 测试类
在测试类中,我们通过 addObserver
方法将商品详情页和购物车添加为观察者,商品库存更新时,它们会自动接收到通知并更新显示。
java
public class WithObserverPattern {
public static void main(String[] args) {
Product product = new Product();
ProductDetailPage detailPage = new ProductDetailPage();
ShoppingCart shoppingCart = new ShoppingCart();
// 注册观察者
product.addObserver(detailPage);
product.addObserver(shoppingCart);
// 设置库存数量,通知观察者
product.setStockQuantity(10);
product.setStockQuantity(5);
}
}
运行结果
plaintext
商品库存更新为: 10
商品详情页更新库存显示为: 10
购物车更新库存显示为: 10
商品库存更新为: 5
商品详情页更新库存显示为: 5
购物车更新库存显示为: 5
优势
- 低耦合性:商品类和观察者类之间只通过接口进行依赖,任何一个模块的变化不会影响到其他模块的实现。
- 可扩展性强 :当需要增加新的依赖商品库存的模块时,只需实现
Observer
接口并注册为观察者,无需修改商品类。 - 符合开闭原则:商品类可以在不修改的情况下增加新的观察者,保持代码的稳定性。
对比总结
特性 | 不使用观察者模式 | 使用观察者模式 |
---|---|---|
耦合性 | 高,商品类直接依赖多个模块(商品详情页、购物车等) | 低,商品类只依赖于 Observer 接口 |
扩展性 | 差,增加新模块需要修改商品类 | 好,新增模块只需实现观察者接口并注册即可 |
维护性 | 差,修改商品类可能导致其他模块发生问题 | 好,模块独立,修改不会影响其他模块 |
符合设计原则 | 不符合开闭原则 | 符合开闭原则 |
结论
通过对比可以看出,观察者模式解决了不使用观察者模式时存在的耦合性高、扩展性差的问题,使得系统更加灵活、可维护和可扩展。在需要处理多个模块之间的依赖关系时,观察者模式提供了一种简洁且有效的方式来解耦不同模块之间的关系。
说点大白话就是采用观察者模式是定义了一个统一的接口,不同的观察者都实现这个接口。然后在被观察的目标中维护一个观察者的集合,若是有改变则指需要增加或者删除对应的观察者即可