在设计模式的世界里,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 等等。不过,万变不离其宗,设计思 路都是差不多的。
简单理解: Subject 代表主动要做的事,比如要更新某个状态,主动触发某个行为。实现Observer 接口,代表被动触发得事,也可以理解为待办事项,比如我修改了某个状态,就要被动得触发一些事情,比如支付成功,就要将订单状态置为已完成等等。我们通过 Observe 中的registerObserver方法将待办事项加入到 Subject 中,然后当 Subject 主动做某项事情时,就会调用notifyObservers方法 将已经添加的待办事项都调用一下。
2、使用场景
1)股票行情应用:股票行情应用中,当股票价格发生变化时,需要通知订阅了该股票得投资者。这里,股票价格更新可以作为被观察者,投资者可以作为观察者。当股票发生变化时,所有订阅了该股票的投资者都会收到通知并更新自己的投资策略。
2)网络聊天室:在网络聊天室中,当有新消息时,需要通知所有在线用户。聊天室服务器可以作为被观察者,用户可以作为观察者。当有新消息时,聊天室服务会通知所有在线用户更新聊天记录。
3)拍卖系统:在拍卖系统中,当出价发生变化时,需要通知所有关注该拍品得用户。这里,拍卖系统可以作为被观察者,用户可以作为观察者。当出价发生变化时,所有关注该拍品得用户都会收到通知并更新自己的出价策略。
4)订阅系统:在订阅系统中,当有新的内容发布时,需要通知所有订阅了该内容得用户,这里,内容发布可以作为被观察者,用户可以作为观察者,当有新内容发布时,所有订阅了该内容得用户都会收到通知并获取最新内容。
5)游戏中的事件系统:在游戏中,当某个事件发生时(如角色升级、道具获得等),可能需要通知多个游戏模块进行相应的处理。这里,游戏事件可以作为被观察者,游戏模块可以作为观察者。当游戏事件发生时,所有关注该事件的游戏模块都会收到通知并执行相应的逻辑。
6)运动比赛实时更新:在体育比赛中,实时更新比分、技术统计等信息对于球迷和分析师非常重要。在这种场景下,比赛数据更新可以作为被观察者,球迷和分析师可以作为观察者。当比赛数据发生变化时,所有关注比赛的的球迷和分析师都会收到通知并更新数据。
7)物联网传感器系统:在物联网(IoT)系统中,有很多传感器不断采集数据,当数据发生变化时,需要通知相关联的设备或系统。在这种场景下,传感器可以作为被观察者,关联的设备或系统可以作为观察者,当传感器发生变化时,所有关联的设备或系统都会收到通知并执行相应的操作。
8)电子邮件通知系统:在一个任务管理系统中,当任务的状态发生变化(如:新任务分配、任务完成等)时,需要通知相关的人员。这里,任务状态变更可以作为被观察者,相关人员可以作为观察者。当任务状态发生变化时,所有关注者都会收到通知并查看任务详情。
9)社交网络:在社交网络中,用户关注其他用户以获取实时动态。当被关注的用户发布新动态时,需要通知所有关注者,在这种场景下,被关注的用户可以作为被观察者,关注者可以作为观察者,当被关注用户发布新动态时,所有关注者都会收到通知并查看动态。
2.1 电商系统的应用
假设有一个电商,当某件商品有促销活动时,需要通知所有订阅了该商品的用户,商品时主题,用户时时观察者。
首先,创建一个Subject接口表示主题:
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
接下来,创建一个Observer接口:
public interface Observer {
void update(String discountInfo);
}
现在,创建一个具体的主题,如Product,实现Subject接口:
public class Product implements Subject {
private ArrayList<Observer> observers;
// 折扣消息
private String discountInfo;
public Product() {
observers = new ArrayList<>();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int index = observers.indexOf(o);
if (index >= 0) {
observers.remove(index);
}
}
public void notifyObservers() {
for (Observer o : observers) {
o.update(discountInfo);
}
}
public void discountChanged() {
notifyObservers();
}
public void setDiscountInfo(String discountInfo) {
this.discountInfo = discountInfo;
discountChanged();
}
}
接着创建一个具体的观察,如User,实现Observer接口:
public class User implements Observer {
private String userName;
private String discountInfo;
private Subject product;
public User(String userName, Subject product) {
this.userName = userName;
this.product = product;
product.registerObserver(this);
}
public void update(String discountInfo) {
this.discountInfo = discountInfo;
display();
}
public void display() {
System.out.println("用户 " + userName + " 收到促销通知: " + discountInfo);
}
}
现在可以创建一个Product实例并向其注册User观察者。当Product的促销信息发生变化时,User会收到通知并显示促销信息。
public class Main {
public static void main(String[] args) {
Product product = new Product();
User user1 = new User("张三", product);
User user2 = new User("李四", product);
// 模拟商品促销信息更新
product.setDiscountInfo("本周末满100减50");
product.setDiscountInfo("双十一全场5折");
}
}
这个例子中,我们创建了一个Product实例并向其注册了两个User观察者。当Product的促销信息发生变化时,所有观察者都会收到通知并更新自己的显示。
2.2 erp
在erp系统中,观察者也有很多应用场景,例如库存管理、生产计划等。假设有一个erp系统,当某个产品的库存低于安全库存时,需要通知采购部门、销售部门和仓库管理员。在这个例子中,产品库存是主题,采购部门、销售部门和仓库管理员是观察者,
我们可以沿用之前定义的Subject 和 Observe 接口:
创建一个具体的主题,如Inventory,实现Subject接口:
public class Inventory implements Subject {
private ArrayList<Observer> observers;
private int stock;
public Inventory() {
observers = new ArrayList<>();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int index = observers.indexOf(o);
if (index >= 0) {
observers.remove(index);
}
}
public void notifyObservers() {
for (Observer o : observers) {
o.update(String.valueOf(stock));
}
}
public void stockChanged() {
notifyObservers();
}
public void setStock(int stock) {
this.stock = stock;
stockChanged();
}
}
接着创建一个具体的观察者,如PurchaseDepartment、SalesDepartment和WarehouseManager,分别实现Observer接口:
public class PurchaseDepartment implements Observer {
private int stock;
private Subject inventory;
public PurchaseDepartment(Subject inventory) {
this.inventory = inventory;
inventory.registerObserver(this);
}
public void update(String stock) {
this.stock = Integer.parseInt(stock);
display();
}
public void display() {
System.out.println("采购部门收到库存更新: " + stock);
}
}
public class SalesDepartment implements Observer {
private int stock;
private Subject inventory;
public SalesDepartment(Subject inventory) {
this.inventory = inventory;
inventory.registerObserver(this);
}
public void update(String stock) {
this.stock = Integer.parseInt(stock);
display();
}
public void display() {
System.out.println("销售部门收到库存更新: " + stock);
}
}
public class WarehouseManager implements Observer {
private int stock;
private Subject inventory;
public WarehouseManager(Subject inventory) {
this.inventory = inventory;
inventory.registerObserver(this);
}
public void update(String stock) {
this.stock = Integer.parseInt(stock);
display();
}
public void display() {
System.out.println("仓库管理员收到库存更新: " + stock);
}
}
public class Main {
public static void main(String[] args) {
Inventory inventory = new Inventory();
PurchaseDepartment purchaseDepartment = new
PurchaseDepartment(inventory);
SalesDepartment salesDepartment = new SalesDepartment(inventory);
WarehouseManager warehouseManager = new
WarehouseManager(inventory);
// 模拟库存变化
inventory.setStock(500);
inventory.setStock(300);
inventory.setStock(100);
}
}
3、发布订阅
发布-订阅模式 和 观察者模式都是用于实现对象间的解耦合的设计模式,尽管它们具有相似之处,但他们在实现方式和适用场景上存在一些关键区别。他们在概念上有一定的观赏性,都是用于实现对象间解耦合通信,可以将 发布-订阅模式看作是观察者模式的一种变体或扩展。
3.1 观察者模式
观察者模式定义了一种一对多的依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖于它的对象(观察者)都会得到通知并自动更新。在这个模式中,被观察者和观察者之间存在直接的关联关系。观察者模式主要包括两类对象:被观察者(Subject)和观察者(Observer)。
3.2 发布-订阅模式
发布-订阅模式(生产者和消费者)于观察者模式类似,但它们之间有一个关键区别:发布订阅模式引入了一个第三方组件(通常为消息代理或事件总线),该组件负责维护发布者和订阅者之间的关系。这意味着发布者和订阅者彼此不直接通信,而是通过消息代理进行通信。这种简洁通信允许发布者和订阅者在运行时动态的添加或删除,从而提高系统的灵活性和可扩展性。
java中的发布-订阅模式示例:
interface Subscriber {
void onEvent(String event);
}
class ConcreteSubscriber implements Subscriber {
@Override
public void onEvent(String event) {
System.out.println("收到事件: " + event);
}
}
// 创建消息总线
class EventBus {
// 使用一个map维护,消息类型和该消息的订阅者
private Map<String, List<Subscriber>> subscribers = new HashMap<>();
// 订阅一个消息
public void subscribe(String eventType, Subscriber subscriber) {
subscribers.computeIfAbsent(eventType, k -> new ArrayList<>
()).add(subscriber);
}
// 接触订阅
public void unsubscribe(String eventType, Subscriber subscriber) {
List<Subscriber> subs = subscribers.get(eventType);
if (subs != null) {
subs.remove(subscriber);
}
}
// 发布事件
public void publish(String eventType, String event) {
List<Subscriber> subs = subscribers.get(eventType);
if (subs != null) {
for (Subscriber subscriber : subs) {
subscriber.onEvent(event);
}
}
}
}
// 使用示例:
public class Main {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
Subscriber subscriber1 = new ConcreteSubscriber();
Subscriber subscriber2 = new ConcreteSubscriber();
// 订阅事件
eventBus.subscribe("eventA", subscriber1);
eventBus.subscribe("eventA", subscriber2);
// 发布事件
eventBus.publish("eventA", "这是事件A的内容");
// 取消订阅
eventBus.unsubscribe("eventA", subscriber1);
// 再次发布事件
eventBus.publish("eventA", "这是事件A的新内容");
}
}
以股票交易系统为例,股票价格的变化会作为事件发布,投资者可以订阅这些股票加个变化事件。当股票价格发生变化时,所有订阅了该股票的投资者都会收到通知。
首先,创建一个Subscriber 接口,用于表示订阅者(投资者):
public interface Subscriber {
void onStockPriceChanged(String stockSymbol, double newPrice);
}
然后创建一个EventBus类,用于管理发布者和订阅者之间的通信:
public class EventBus {
private Map<String, List<Subscriber>> subscribers = new HashMap<>();
// 订阅股票价格变化事件
public void subscribe(String stockSymbol, Subscriber subscriber) {
subscribers.computeIfAbsent(stockSymbol, k -> new ArrayList<>
()).add(subscriber);
}
// 取消订阅股票价格变化事件
public void unsubscribe(String stockSymbol, Subscriber subscriber) {
List<Subscriber> subs = subscribers.get(stockSymbol);
if (subs != null) {
subs.remove(subscriber);
}
}
// 发布股票价格变化事件
public void publish(String stockSymbol, double newPrice) {
List<Subscriber> subs = subscribers.get(stockSymbol);
if (subs != null) {
for (Subscriber subscriber : subs) {
subscriber.onStockPriceChanged(stockSymbol, newPrice);
}
}
}
}
然后创建一个具体的订阅者实现,例如Investor类:
public class Investor implements Subscriber {
private String name;
public Investor(String name) {
this.name = name;
}
// 股票代号,新价格
@Override
public void onStockPriceChanged(String stockSymbol, double newPrice) {
System.out.println(name + " 收到股票 " + stockSymbol + " 价格变化通知,新
价格:" + newPrice);
}
}
最后定义一个股票类:
// 股票
public class Stock{
private double stockPrice;
public void stockPriceChanged(double newPrice) {
this.stockPrice = newPrice;
eventBus.publish("AAPL", 210.0);
}
}
· 使用示例:
public class Main {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
Investor investor1 = new Investor("投资者A");
Investor investor2 = new Investor("投资者B");
// 订阅股票价格变化事件
eventBus.subscribe("AAPL", investor1);
eventBus.subscribe("AAPL", investor2);
Stock stock = new Stock();
stock.stockPriceChanged(21.5);
// 取消订阅股票价格变化事件
eventBus.unsubscribe("AAPL", investor1);
// 再次发布股票价格变化事件
stock.stockPriceChanged(21.5);
}
}
这个示例中,Investor类实现了Subscriber接口,代表投资者。EventBus类是负责管理发布者和订阅者之间通信的中间组件。投资者可以通过EventBus订阅股票价格。
4、源码使用
4.1 jdk中的观察者
java.util.Observable类实现了主题Subject的功能。而java.util.Observer接口则定义了观察者Observer的方法。
通过调用Observable对象的notifyObservers()方法,可以通知所有注册的Observer对象,让他们更新自己的状态。
以下是一个使用案例:假设有一个银行账户类,它的余额是可变的,当余额发生变化时,需要通知所有的观察者(比如银行客户),以便他们更新自己的显示信息。
// 银行账户类
public class BankAccount extends Observable {
private double balance;
// 构造函数
public BankAccount(double balance) {
this.balance = balance;
}
// 存款操作
public void deposit(double amount) {
balance += amount;
setChanged(); // 表示状态已经改变
notifyObservers(); // 通知所有观察者
}
// 取款操作
public void withdraw(double amount) {
balance -= amount;
setChanged(); // 表示状态已经改变
notifyObservers(); // 通知所有观察者
}
// 获取当前余额
public double getBalance() {
return balance;
}
// 主函数
public static void main(String[] args) {
BankAccount account = new BankAccount(1000.0);
// 创建观察者
Observer observer1 = new Observer() {
@Override
public void update(Observable o, Object arg) {
System.out.println("客户1: 余额已更新为 " +
((BankAccount)o).getBalance());
}
};
Observer observer2 = new Observer() {
@Override
public void update(Observable o, Object arg) {
System.out.println("客户2: 余额已更新为 " +
((BankAccount)o).getBalance());
}
};
// 注册观察者
account.addObserver(observer1);
account.addObserver(observer2);
// 存款操作,触发观察者更新
account.deposit(100.0);
// 取款操作,触发观察者更新
account.withdraw(50.0);
}
}
这个案例中,BankAccount类继承了java.util.Observable类,表示它是一个主题 (Subject)。在存款或取款操作时,它会调用setChanged()方法表示状态已经改 变,并调用notifyObservers()方法通知所有观察者(Observer)。
<dependency><groupId> com.google.guava </groupId><artifactId> guava </artifactId><version> 30.1-jre </version></dependency>
public class MessageEvent {
private String message;
public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
public class MessageSubscriber {
@Subscribe
public void handleMessageEvent(MessageEvent event) {
System.out.println("收到消息: " + event.getMessage());
}
}
最后,我们来看一个使用案例:
public class Main {
public static void main(String[] args) {
// 创建 EventBus 实例
EventBus eventBus = new EventBus();
// 创建并注册订阅者
MessageSubscriber subscriber = new MessageSubscriber();
eventBus.register(subscriber);
// 发布事件
eventBus.post(new MessageEvent("Hello, EventBus!"));
// 取消注册订阅者
eventBus.unregister(subscriber);
// 再次发布事件(此时订阅者已取消注册,将不会收到消息)
eventBus.post(new MessageEvent("Another message"));
}
}
5、进阶
public interface Observer {
void update(String message);
}
public interface Observable {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String message);
}
实现一个具体的被观察者类Subject 和 一个具体的观察者类ConcreteObserver
public class Subject implements Observable {
private List<Observer> observers;
private ExecutorService executorService;
public Subject() {
observers = new ArrayList<>();
executorService = Executors.newCachedThreadPool();
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
executorService.submit(() -> observer.update(message));
}
}
public void setMessage(String message) {
notifyObservers(message);
}
}
public class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
最后创建一个简单的示例来测试实现的异步非阻塞观察者模式:
public class Main {
public static void main(String[] args) {
Subject subject = new Subject();
ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
ConcreteObserver observer2 = new ConcreteObserver("Observer 2");
ConcreteObserver observer3 = new ConcreteObserver("Observer 3");
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.addObserver(observer3);
subject.setMessage("Hello, observers!");
// 等待异步任务完成
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
二、模板模式
模板模式主要是用来解决复用和扩展两个问题
1、原理与实现
模板模式,全称是模板方法设计模式,英文是Template Method Design Pattern.在GoF的 设计模式一书中,它是这么定义的:
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
翻译成中文就是:模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。
这里的 算法 ,可以理解为广义上的 业务逻辑,并不特指数据结构和算法中的 算法。这里的算法骨架的方法就是模板方法,这也是模板方法模式名字的由来。
代码示例:
1、首先创建一个抽象类,定义算法的骨架:
public abstract class AbstractTemplate {
// 模板方法,定义算法的骨架
public final void templateMethod() {
step1();
step2();
step3();
}
// 基本方法,定义算法中不会变化的步骤
private void step1() {
System.out.println("Step 1: Prepare the ingredients.");
}
// 抽象方法,定义算法中需要子类实现的步骤
protected abstract void step2();
// 基本方法,定义算法中不会变化的步骤
private void step3() {
System.out.println("Step 3: Serve the dish.");
}
}
2、然后创建具体的子类,实现抽象类中定义的抽象方法:
public class ConcreteTemplateA extends AbstractTemplate {
@Override
protected void step2() {
System.out.println("Step 2 (A): Cook the dish using method A.");
}
}
public class ConcreteTemplateB extends AbstractTemplate {
@Override
protected void step2() {
System.out.println("Step 2 (B): Cook the dish using method B.");
}
}
public class Main {
public static void main(String[] args) {
AbstractTemplate templateA = new ConcreteTemplateA();
AbstractTemplate templateB = new ConcreteTemplateB();
System.out.println("Using Template A:");
templateA.templateMethod();
System.out.println("\nUsing Template B:");
templateB.templateMethod();
}
}
vbnetCopy codeUsing Template A:Step 1: Prepare the ingredients.Step 2 (A): Cook the dish using method A.Step 3: Serve the dish.Using Template B:Step 1: Prepare the ingredients.Step 2 (B): Cook the dish using method B.Step 3: Serve the dish.
这个例子中, AbstractTemplate 是一个抽象类,它定义了一个名为 templateMethod 的模板方法。该方法包含三个步骤: step1 、 step2 和 step3 。其中, step1 和 step3 是基本方法,它们的实现在抽象类中定义且不会改变。 step2 是一个抽象方法,需要子类(如 ConcreteTemplateA 和 ConcreteTemplateB )根据具体需求实现。客户端代码通过创建子类的实例并调用 templateMethod 方法来执行算法。
2、源码中的作用
2.1 复用
模板模式把一个算法中不变的流程抽象到父类的模板方法 templateMethod() 中,将可变的部分step2() 留给子类来实现。所有子类都可以复用父类中的模板方法定义的流程代码。
1)java InputStream
java IO 类库中,有很多类的设计用到模板模式,比如InputStream、OutputStream、Reader、Writer。我们拿 InputStream 来举例说明一下。
public abstract class InputStream implements Closeable {
//...省略其他代码...
public int read(byte b[], int off, int len) throws IOException {
Objects.checkFromIndexSize(off, len, b.length);
if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
public abstract int read() throws IOException;
}
// 这里有一个具体的实现类。用于从一个字节缓冲区中读取一个字节。方法的签名和
功能如下:
public class ByteArrayInputStream extends InputStream {
//...省略其他代码...
@Override
public synchronized int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
}
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("Hello World.");
}
}
<servlet>
<servlet-name> HelloServlet </servlet-name><servlet-class> com.xzg.cd.HelloServlet </servlet-class></servlet><servlet-mapping><servlet-name> HelloServlet </servlet-name><url-pattern> /hello </url-pattern></servlet-mapping>
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
// 子类实现的扩展点
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
// 子类实现的扩展点
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
// 子类实现的扩展点
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
// 子类实现的扩展点
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
// 子类实现的扩展点
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
// 子类实现的扩展点
doTrace(req,resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
3、应用场景
public abstract class PaymentProcessor {
// 模板方法,定义支付流程骨架
public final void processPayment(Order order) {
// 获取支付方式(如信用卡、支付宝、微信等)
String paymentMethod = getPaymentMethod();
// 验证订单信息(如订单金额、收货地址等)
validateOrder(order);
// 验证支付信息(如支付账号、支付密码等)
validatePaymentInfo(paymentMethod);
// 执行支付
executePayment(paymentMethod, order);
// 发送支付通知
sendPaymentNotification(order);
}
// 获取支付方式,具体实现由子类提供
protected abstract String getPaymentMethod();
// 验证订单信息,通用逻辑
private void validateOrder(Order order) {
// 验证订单信息的实现
}
// 验证支付信息,具体实现由子类提供
protected abstract void validatePaymentInfo(String paymentMethod);
// 执行支付,具体实现由子类提供
protected abstract void executePayment(String paymentMethod, Order
order);
// 发送支付通知,通用逻辑
private void sendPaymentNotification(Order order) {
// 发送支付通知的实现
}
}
public class AlipayProcessor extends PaymentProcessor {
@Override
protected String getPaymentMethod() {
return "Alipay";
}
@Override
protected void validatePaymentInfo(String paymentMethod) {
// 验证支付宝支付信息的实现
}
@Override
protected void executePayment(String paymentMethod, Order order) {
// 执行支付宝支付的实现
}
}
public class WechatPayProcessor extends PaymentProcessor {
@Override
protected String getPaymentMethod() {
return "WechatPay";
}
@Override
protected void validatePaymentInfo(String paymentMethod) {
// 验证微信支付信息的实现
}
@Override
protected void executePayment(String paymentMethod, Order order) {
// 执行微信支付的实现
}
}
public class PaymentService {
public void processPayment(Order order, String paymentType) {
PaymentProcessor paymentProcessor;
if ("Alipay".equalsIgnoreCase(paymentType)) {
paymentProcessor = new AlipayProcessor();
} else if ("WechatPay".equalsIgnoreCase(paymentType)) {
paymentProcessor = new WechatPayProcessor();
} else {
throw new IllegalArgumentException("Unsupported payment type: " +
paymentType);
}
paymentProcessor.processPayment(order);
}
}
4、Callback 回调
1)设计范式:模板方法设计模式通常用于面向对象编程(OOP),它依赖于继承和多态来实现代码复用。回调函数则通常用于函数式编程,它通过将函数作为参数传递给其他函数来实现代码复用。
2)实现方式:模板方法设计依赖于抽象类和子类之间的继承关系。在抽象类中定义一个算法的骨架,并将某些步骤延迟到子类中实现。而回调函数通过将一个函数作为参数传递给另一个函数,让调用者可以自定义特定的行为。
1)目的 :模板方法设计模式和回调函数都旨在将变化的部分于不变的部分分离,提高代码的复用性和可维护性。
2)实现相互关系:在某些情况下,模板方法设计模式可以通过回调函数来实现。例如,在java中,可以使用匿名内部类或者lambda表达式作为回调函数,实现模板方法设计的目标,类似的在,在面向对象的语言中,回调函数也可以通过模板方法设计模式来实现。
代码如下所示:
public interface ICallback {
void methodToCallback();
}
public class BClass {
public void process(ICallback callback) {
//...
callback.methodToCallback();
//...
}
}
public class AClass {
public static void main(String[] args) {
BClass b = new BClass();
b.process(new ICallback() { //回调对象
@Override
public void methodToCallback() {
System.out.println("Call back me.");
}
});
}
}
三、策略模式
1、原理和实现
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it。
翻译成中文就是:定义一族算法类,将每个算法分别封装起来,让它们可以互相替换,策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。
策略模式主要包含一下角色:
1)策略接口(Strategy):定义所有支持的算法的公共接口,客户端使用这个接口与具体策略进行交互。
2)具体策略(Concrete Strategy):实现策略接口的具体策略类,这些类封装了实际的算法逻辑。
3)上下文(Context):持有一个策略对象,用于与客户端进行交互,上下文可以定义一些接口,让客户端不直接与策略接口交互,从而实现策略的封装。
例子:假设我们要实现一个计算器,支持加法、减法和乘法运算。我们可以使用策略模式将各种运算独立为不同的策略,并让客户端根据需要选择和使用不同的策略。
首先我们定义一个策略接口:
public interface Operation {
double execute(double num1, double num2);
}
接下来,我们创建具体的策略类来实现加法、减法和乘法运算:
public class Addition implements Operation {
@Override
public double execute(double num1, double num2) {
return num1 + num2;
}
}
public class Subtraction implements Operation {
@Override
public double execute(double num1, double num2) {
return num1 - num2;
}
}
public class Multiplication implements Operation {
@Override
public double execute(double num1, double num2) {
return num1 * num2;
}
}
public class Calculator {
private Operation operation;
public void setOperation(Operation operation) {
this.operation = operation;
}
public double executeOperation(double num1, double num2) {
return operation.execute(num1, num2);
}
}
现在,客户端可以使用 Calculator 类来执行不同的运算,例如:
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator();
calculator.setOperation(new Addition());
System.out.println("10 + 5 = " + calculator.executeOperation(10, 5));
calculator.setOperation(new Subtraction());
System.out.println("10 - 5 = " + calculator.executeOperation(10, 5));
calculator.setOperation(new Multiplication());
System.out.println("10 * 5 = " + calculator.executeOperation(10,5));
}
}
在这个例子中,我们使用策略模式将加法、减法和乘法运算独立为不同的策略。客户端可以根据需要选择和使用不同的策略。 Calculator 上下文类持有一个Operation 策略对象,并通过 setOperation 方法允许客户端设置所需的策略。这种方式使得算法的选择和执行更加灵活,易于扩展和维护
1.1 策略的定义
策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。因为所有的策略类都实现相同的接口,所以,客户端代码基于接口而非实现编程,可以灵活的替换不同的策略,示例:
public interface Strategy {
void algorithmInterface();
}
public class ConcreteStrategyA implements Strategy {
@Override
public void algorithmInterface() {
//具体的算法...
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void algorithmInterface() {
//具体的算法...
}
}
1.2 策略的创建
因为策略模式会包含一组策略,在使用它们的时候,一般会通过类型(type)来判断创建哪个策略来使用。为了封装创建逻辑,我们需要对客户端代码屏蔽创建细节。
事实上我们可以做一定的优化,可以根据type创建策略的逻辑抽离出来,放到工厂类中,示例代码:
public class StrategyFactory {
private static final Map<String, Strategy> strategies = new HashMap<>();
static {
strategies.put("A", new ConcreteStrategyA());
strategies.put("B", new ConcreteStrategyB());
}
public static Strategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
return strategies.get(type);
}
}
一般来讲,如果策略类是无状态的,不包含成员变量,只是纯粹的算法实现,这样的策略对象是可以被共享使用的,不需要在每次调用 getStrategy() 的时候,都创建一个新的策略对象。针对这种情况,我们可以使用上面这种工厂类的实现方式,事先创建好每个策略对象,缓存到工厂类中,用的时候直接返回。
public class StrategyFactory {
public static Strategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
if (type.equals("A")) {
return new ConcreteStrategyA();
} else if (type.equals("B")) {
return new ConcreteStrategyB();
}
return null;
}
}
1.3 策略的使用
// 策略接口:EvictionStrategy
// 策略类:LruEvictionStrategy、FifoEvictionStrategy、
LfuEvictionStrategy...
// 策略工厂:EvictionStrategyFactory
public class UserCache {
private Map<String, User> cacheData = new HashMap<>();
private EvictionStrategy eviction;
public UserCache(EvictionStrategy eviction) {
this.eviction = eviction;
}
//...
}
// 运行时动态确定,根据配置文件的配置决定使用哪种策略
public class Application {
public static void main(String[] args) throws Exception {
EvictionStrategy evictionStrategy = null;
Properties props = new Properties();
props.load(new FileInputStream("./config.properties"));
String type = props.getProperty("eviction_type");
evictionStrategy = EvictionStrategyFactory.getEvictionStrategy(type);
UserCache userCache = new UserCache(evictionStrategy);
//...
}
}
// 非运行时动态确定,在代码中指定使用哪种策略
public class Application {
public static void main(String[] args) {
//...
EvictionStrategy evictionStrategy = new LruEvictionStrategy();
UserCache userCache = new UserCache(evictionStrategy);
//...
}
}
2、优化if分支
2.1 基础优化
我们将以解析报文的例子为基础,展示如何使用策略模式优化具有if条件分支的代码。
示例:
public class MessageParser {
public void parseMessage(Message message) {
String messageType = message.getType();
if ("XML".equalsIgnoreCase(messageType)) {
// 解析 XML 报文
System.out.println("解析 XML 报文: " + message.getContent());
} else if ("JSON".equalsIgnoreCase(messageType)) {
// 解析 JSON 报文
System.out.println("解析 JSON 报文: " + message.getContent());
} else if ("CSV".equalsIgnoreCase(messageType)) {
// 解析 CSV 报文
System.out.println("解析 CSV 报文: " + message.getContent());
} else {
throw new IllegalArgumentException("未知的报文类型: " + messageType);
}
}
}
public interface MessageParserStrategy {
// 解析报文内容的方法,输入一个 Message 对象,无返回值
void parse(Message message);
}
然后,实现xml、json、csv报文解析策略:
// XML 报文解析策略
public class XmlMessageParserStrategy implements MessageParserStrategy {
@Override
public void parse(Message message) {
System.out.println("解析 XML 报文: " + message.getContent());
}
}
// JSON 报文解析策略
public class JsonMessageParserStrategy implements MessageParserStrategy {
@Override
public void parse(Message message) {
System.out.println("解析 JSON 报文: " + message.getContent());
}
}
// CSV 报文解析策略
public class CsvMessageParserStrategy implements MessageParserStrategy {
@Override
public void parse(Message message) {
System.out.println("解析 CSV 报文: " + message.getContent());
}
}
接下来创建一个MessageParserContext类,该类将根据传入的策略解析报文:
public class MessageParserContext {
private MessageParserStrategy strategy;
// 设置报文解析策略
public void setStrategy(MessageParserStrategy strategy) {
this.strategy = strategy;
}
// 根据策略解析报文
public void parseMessage(Message message) {
strategy.parse(message);
}
}
最后,使用策略模式进行报文解析,避免了分支判断:
public class Main {
public static void main(String[] args) {
MessageParserContext parserContext = new MessageParserContext();
// 使用 XML 报文解析策略
parserContext.setStrategy(new XmlMessageParserStrategy());
parserContext.parseMessage(new Message("XML", "<xml>这是一个 XML 报文</xml>"));
// 使用 JSON 报文解析策略
parserContext.setStrategy(new JsonMessageParserStrategy());
parserContext.parseMessage(new Message("JSON", "{\"message\": \"这是一个 JSON 报文\"}"));
// 使用 CSV 报文解析策略
parserContext.setStrategy(new CsvMessageParserStrategy());
parserContext.parseMessage(new Message("CSV", "这是一个,CSV,报文"));
}
}
2.2 结合工厂模式:
我们可以将策略模式与工厂模式结合,以便根据不同的消息类型自动匹配不同的解析策略。下面是如何实现这个优化的:
首先,我们创建一个 MessageParserStrategyFactory 类,用于根据报文类型创建相应的解析策略:
public class MessageParserStrategyFactory {
private static final Map<String, MessageParserStrategy> strategies = new
HashMap<>();
static {
strategies.put("XML", new XmlMessageParserStrategy());
strategies.put("JSON", new JsonMessageParserStrategy());
strategies.put("CSV", new CsvMessageParserStrategy());
}
public static MessageParserStrategy getStrategy(String messageType) {
MessageParserStrategy strategy =
strategies.get(messageType.toUpperCase());
if (strategy == null) {
throw new IllegalArgumentException("未知的报文类型: " + messageType);
}
return strategy;
}
}
接下来,我们修改 MessageParserContext 类,使其根据报文类型自动选择解析策略:
public class MessageParserContext {
public void parseMessage(Message message) {
MessageParserStrategy strategy =
MessageParserStrategyFactory.getStrategy(message.getType());
strategy.parse(message);
}
}
public class Main {
public static void main(String[] args) {
MessageParserContext parserContext = new MessageParserContext();
// 自动使用 XML 报文解析策略
parserContext.parseMessage(new Message("XML", "<xml>这是一个 XML 报文</xml>"));
// 自动使用 JSON 报文解析策略
parserContext.parseMessage(new Message("JSON", "{\"message\": \"这是一个 JSON 报文\"}"));
// 自动使用 CSV 报文解析策略
parserContext.parseMessage(new Message("CSV", "这是一个,CSV,报文"));
}
}
3、使用场景
public interface PaymentStrategy {
// 支付操作
void pay(double amount);
}
接着,我们为信用卡支付方式提供一个具体实现CreditCardPaymentStrategy :
public class CreditCardPaymentStrategy implements PaymentStrategy {
private String name;
private String cardNumber;
private String cvv;
private String expirationDate;
public CreditCardPaymentStrategy(String name, String cardNumber, String
cvv, String expirationDate) {
this.name = name;
this.cardNumber = cardNumber;
this.cvv = cvv;
this.expirationDate = expirationDate;
}
@Override
public void pay(double amount) {
System.out.println("使用信用卡支付:" + amount + "元");
}
}
public class AlipayPaymentStrategy implements PaymentStrategy {
private String email;
private String password;
public AlipayPaymentStrategy(String email, String password) {
this.email = email;
this.password = password;
}
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付:" + amount + "元");
}
}
现在,我们可以在客户端代码中根据用户选择的支付方式来调用相应的支付策略:
public class PaymentClient {
public static void main(String[] args) {
// 创建信用卡支付策略实例
PaymentStrategy creditCardStrategy = new CreditCardPaymentStrategy("张三", "1234567890123456", "123", "12/23");
// 创建支付宝支付策略实例
PaymentStrategy alipayStrategy = new AlipayPaymentStrategy("zhangsan@example.com", "mypassword");
// 根据用户选择的支付方式进行支付
double paymentAmount = 100.0;
System.out.println("用户选择信用卡支付:");
creditCardStrategy.pay(paymentAmount);
System.out.println("用户选择支付宝支付:");
alipayStrategy.pay(paymentAmount);
}
}
四、职责链模式
1、原理和实现
职责链模式的英文翻译是 Chain Of Responsibility Design Pattern,在 GoF 的 设计模式中,他是这么定义的:
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
翻译成中文就是:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上某个接收对象能够处理它,实际上,我们的责任链并不是和概念中的完全一样。
原始概念中,是直到链上的某个接收对象能够处理它为止。
实际使用中,链上的所有对象都可以对请求进行特殊处理。
2、实现方式
2.1 使用链表实现
第一种实现方式如下所示。其中,Handler 是所有处理器类的抽象父类,handle() 是抽象方法。每个具体的处理器类(HandlerA、HandlerB)的 handle() 函数的代码结构类似,如果它能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理(也就是调用 successor.handle())。HandlerChain 是处理器链,从数据结构的角度来看,它就是一个记录了链头、链尾的链表。其中,记录链尾是为了方便添加处理器。
public abstract class Handler {
protected Handler successor = null;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handle();
}
public class HandlerA extends Handler {
@Override
public boolean handle() {
boolean handled = false;
//...
if (!handled && successor != null) {
successor.handle();
}
}
}
public class HandlerB extends Handler {
@Override
public void handle() {
boolean handled = false;
//...
if (!handled && successor != null) {
successor.handle();
}
}
}
public class HandlerChain {
private Handler head = null;
private Handler tail = null;
public void addHandler(Handler handler) {
handler.setSuccessor(null);
if (head == null) {
head = handler;
tail = handler;
return;
}
tail.setSuccessor(handler);
tail = handler;
}
public void handle() {
if (head != null) {
head.handle();
}
}
}
// 使用举例
public class Application {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerA());
chain.addHandler(new HandlerB());
chain.handle();
}
}
实际上,上面的代码实现不够优雅。处理器类的 handle() 函数,不仅包含自己的业 务逻辑,还包含对下一个处理器的调用,也就是代码中的 successor.handle()。一个不熟悉这种代码结构的程序员,在添加新的处理器类的时候,很有可能忘记在handle() 函数中调用 successor.handle(),这就会导致代码出现 bug。
针对这个问题,我们对代码进行重构,利用模板模式,将调用 successor.handle() 的逻辑从具体的处理器类中剥离出来,放到抽象父类中。这样具体的处理器类只需要实现自己的业务逻辑就可以了。重构之后的代码如下所示:
public abstract class Handler {
protected Handler successor = null;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public final void handle() {
boolean handled = doHandle();
if (successor != null && !handled) {
successor.handle();
}
}
protected abstract boolean doHandle();
}
public class HandlerA extends Handler {
@Override
protected boolean doHandle() {
boolean handled = false;
//...
return handled;
}
}
public class HandlerB extends Handler {
@Override
protected boolean doHandle() {
boolean handled = false;
//...
return handled;
}
}
// HandlerChain和Application代码不变
2.2 使用数组实现
HandlerChain 类用数组而非链表来保存所有的处理器,并且需要在 HandlerChain的 handle() 函数中,依次调用每个处理器的 handle() 函数。
public interface IHandler {
boolean handle();
}
public class HandlerA implements IHandler {
@Override
public boolean handle() {
boolean handled = false;
//...
return handled;
}
}
public class HandlerB implements IHandler {
@Override
public boolean handle() {
boolean handled = false;
//...
return handled;
}
}
public class HandlerChain {
private List<IHandler> handlers = new ArrayList<>();
public void addHandler(IHandler handler) {
this.handlers.add(handler);
}
public void handle() {
for (IHandler handler : handlers) {
boolean handled = handler.handle();
if (handled) {
break;
}
}
}
}
// 使用举例
public class Application {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerA());
chain.addHandler(new HandlerB());
chain.handle();
}
}
2.3 扩展
在 GoF 给出的定义中,如果处理器链上的某个处理器能够处理这个请求,那就不会继续往下传递请求。实际上,职责链模式还有一种变体,那就是请求会被所有的处理器都处理一遍,不存在中途终止的情况。这种变体也有两种实现方式:用链表存储处理器和用数组存储处理器,跟上面的两种实现方式类似,只需要稍微修改即可。
我这里只给出其中一种实现方式,如下所示。另外一种实现方式你对照着上面的实现自行修改。
public abstract class Handler {
protected Handler successor = null;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public final void handle() {
doHandle();
if (successor != null) {
successor.handle();
}
}
protected abstract void doHandle();
}
public class HandlerA extends Handler {
@Override
protected void doHandle() {
//...
}
}
public class HandlerB extends Handler {
@Override
protected void doHandle() {
//...
}
}
public class HandlerChain {
private Handler head = null;
private Handler tail = null;
public void addHandler(Handler handler) {
handler.setSuccessor(null);
if (head == null) {
head = handler;
tail = handler;
return;
}
tail.setSuccessor(handler);
tail = handler;
}
public void handle() {
if (head != null) {
head.handle();
}
}
}
// 使用举例
public class Application {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerA());
chain.addHandler(new HandlerB());
chain.handle();
}
}
3、源码实现
Servlet Filter、Spring Interceptor、mybtias的插件 源码中都包含有责任连模式,可以自行查阅一下相关资料。
4、应用场景
责任链模式在工作中的应用广泛,以下列举了一些常见的使用场景,大家可以自行学习:
1. 日志记录器:在应用程序中,我们可能需要将日志记录到不同的位置,如控制台、文件、数据库等。我们可以创建一个日志记录器链,每个记录器处理特定级别的日志,然后将请求传递给下一个记录器。这样,可以根据日志级别灵活地记录日志信息。
2. Web 应用中的过滤器和拦截器:在 Web 应用程序中,我们经常需要对请求进行预处理和后处理,如身份验证、授权、编码转换、请求日志记录等。过滤器和拦截器就是典型的使用责任链模式的场景,请求和响应在过滤器或拦截器链中依次传递,每个过滤器或拦截器执行特定的任务。
3. 工作流引擎:在一个工作流引擎中,一个请求可能需要经过多个处理步骤,这些步骤可以看作是一个责任链。每个处理器处理请求的一个部分,然后将请求传递给下一个处理器,直到请求被完全处理。
4. 软件审批流程:在企业软件开发过程中,代码审查、需求审批、文档审查等流程可能需要多个审批者按顺序审批。这种场景下,责任链模式能够确保每个审批者只关注自己的审批职责,并将审批请求传递给下一个审批者。
5. 电子邮件处理:在一个电子邮件处理系统中,可能需要对不同类型的邮件进行不同的处理,如垃圾邮件过滤、自动回复、邮件归类等。在这种情况下,可以使用 责任链模式来创建一个邮件处理链,每个处理器负责处理特定类型的邮件,然后将邮件传递给下一个处理器。
6. 事件处理系统:在一个事件驱动的系统中,可能需要对不同类型的事件进行不同的处理。责任链模式可以用来创建一个事件处理器链,每个处理器负责处理特定类型的事件,并将事件传递给下一个处理器。这样可以确保系统的可扩展性和灵活性。
7. 规则引擎:在某些业务场景下,可能需要按照一定的规则对数据进行处理。规则引擎是典型的使用责任链模式的场景。每个规则可以看作是一个处理器,对数据进行特定的处理,然后将数据传递给下一个规则,直至所有规则都被执行。
8. 任务调度系统:在任务调度系统中,根据任务的优先级、类型和资源需求,可能需要将任务分配给不同的执行器。责任链模式可以确保每个执行器只关注自己可以处理的任务,并将其他任务传递给下一个执行器。
除了以上的使用场景,我们再给大家列举一个使用场景,如下:
对于支持 UGC(User Generated Content,用户生成内容)的应用(比如论坛)来说,用户生成的内容(比如,在论坛中发表的帖子)可能会包含一些敏感词(比如涉黄、广告、反动等词汇)。针对这个应用场景,我们就可以利用职责链模式来过滤这些敏感词。
在这个应用场景中,我们可以创建一个过滤器链来过滤用户生成的内容。每个过滤器负责处理一种类型的敏感词,然后将内容传递给下一个过滤器。以下是一个简单的实现示例:
首先,我们定义一个过滤器接口
public interface ContentFilter {
String filter(String content);
}
然后,我们实现不同类型的过滤器,例如涉黄过滤器、广告过滤器和反动过滤器:
// 涉黄过滤器
public class PornographyFilter implements ContentFilter {
@Override
public String filter(String content) {
// 这里用简单的字符串替换来表示过滤操作,实际应用中需要更复杂的过滤逻辑
return content.replaceAll("涉黄词汇", "***");
}
}
// 广告过滤器
public class AdvertisementFilter implements ContentFilter {
@Override
public String filter(String content) {
return content.replaceAll("广告词汇", "***");
}
}
// 反动过滤器
public class ReactionaryFilter implements ContentFilter {
@Override
public String filter(String content) {
return content.replaceAll("反动词汇", "***");
}
}
接下来,我们创建一个过滤器链:
public class FilterChain {
private List<ContentFilter> filters = new ArrayList<>();
public FilterChain addFilter(ContentFilter filter) {
filters.add(filter);
return this;
}
public String doFilter(String content) {
for (ContentFilter filter : filters) {
content = filter.filter(content);
}
return content;
}
}
public class Main {
public static void main(String[] args) {
// 创建一个过滤器链
FilterChain filterChain = new FilterChain();
filterChain.addFilter(new PornographyFilter())
.addFilter(new AdvertisementFilter())
.addFilter(new ReactionaryFilter());
// 用户生成的内容
String userContent = "这里有一些涉黄词汇,这里有一些广告词汇,这里有一些反动词汇。";
// 使用过滤器链处理内容
String filteredContent = filterChain.doFilter(userContent);
System.out.println(filteredContent);
}
}