一、前言
在上两篇 《深入了解JAVA中的23种设计模式(一)- 创建型模式》 《深入了解JAVA中的23种设计模式(二)- 结构型模式》 中介绍了Java中的23种设计模式的创建型模式与结构型模式中的一些设计模式,本文将继续介绍设计模式中的行为型模式。
二、行为型模式(上)
1. 策略模式
1.1 简介
在运行时改变对象的内部行为。
1.2 使用场景
当存在多种算法,客户端会根据不同的条件选择使用不同的算法时,可以使用策略模式。
如果一个类中使用了大量的条件语句来选择不同的行为,那么可以考虑使用策略模式,将每一种行为都封装到一个策略类中。
当算法的逻辑非常复杂,且需要被多次使用时,可以考虑使用策略模式,将算法封装到独立的策略类中,以便管理和维护。
1.3 代码示例
// 策略接口
public interface Strategy {
int doOperation(int num1, int num2);
}
// 具体策略A - 实现加法
public class OperationAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
// 具体策略B - 实现减法
public class OperationSubtract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
// 上下文类,持有一个策略对象的引用
public class Context {
private Strategy strategy;
// 构造函数,传入一个策略对象
public Context(Strategy strategy) {
this.strategy = strategy;
}
// 执行策略
public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
2. 模板方法模式
2.1 简介
模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。在模板方法模式中,通常有一个抽象类,它定义了一个或多个抽象操作(即模板方法中的某些步骤),以及一个或多个具体操作(即模板方法中的非抽象步骤)。子类可以覆盖一个或多个抽象操作以改变算法的部分行为,但算法的总体结构(即模板方法)保持不变。
2.2 使用场景
当多个类只在其操作中的某些特定步骤上有所不同,但整体算法框架相同时,可以使用模板方法模式来复用这些算法框架。
当算法的实现比较复杂,并且需要被多次调用时,可以使用模板方法模式来将算法分解为若干个步骤,并将这些步骤的实现分散到不同的子类中。
在需要经常扩展功能的系统中,可以使用模板方法模式来定义可扩展的算法框架,使得新的功能可以通过添加新的子类来实现。
2.3 代码示例
// 抽象类(模板)
public abstract class AbstractClass {
// 模板方法
public final void templateMethod() {
specificMethod1();
specificMethod2();
// 调用具体方法,但此处不直接调用,而是让子类实现
hookMethod();
}
// 具体方法
protected void specificMethod1() {
System.out.println("执行具体方法1");
}
protected void specificMethod2() {
System.out.println("执行具体方法2");
}
// 钩子方法,默认为空实现,子类可以覆盖
protected void hookMethod() {
// 空实现
}
}
// 子类A
public class SubclassA extends AbstractClass {
@Override
protected void hookMethod() {
System.out.println("子类A的钩子方法实现");
}
}
// 子类B
public class SubclassB extends AbstractClass {
// 子类B可以选择不覆盖钩子方法,使用默认实现
}
3. 观察者模式
3.1 简介
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,它的所有依赖者(观察者)都会自动收到通知并更新。
在观察者模式中,通常包含以下两种角色:
主题(Subject):维护了观察者对象的列表,可以注册、移除观察者,并通知观察者状态的变化。
观察者(Observer):实现了更新接口,以便在得到主题的通知时更新自己。
3.2 使用场景
当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。
如应用内消息通知、邮件订阅、新闻推送等。
在 MVC 或 MVVM 架构中,视图(View)通常是观察者,模型(Model)是主题,当模型数据发生变化时,视图会自动更新。
3.3 代码示例
// 观察者接口
interface Observer {
void update(String message);
}
// 具体观察者
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " 接收到消息: " + message);
}
}
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String message);
}
// 具体主题
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String state;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
// 其他业务逻辑方法,用于修改状态并通知观察者
public void someBusinessLogic() {
state = "状态已改变";
notifyObservers(state);
}
}
4. 迭代器模式
4.1 简介
顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式为遍历不同的聚合结构(如列表、栈、树等)提供了一个统一的接口,从而支持以相同的方式遍历这些不同的聚合结构。
迭代器模式主要包含以下角色:
迭代器(Iterator):定义访问和遍历元素的接口。
具体迭代器(Concrete Iterator):实现迭代器的接口,并追踪遍历中的当前位置。
聚合(Aggregate):定义创建迭代器对象的接口。
具体聚合(Concrete Aggregate):实现聚合接口,返回具体迭代器的实例。
4.2 使用场景
当你想遍历一个聚合对象(如列表、集合等)并访问其元素,但又不想暴露该对象的内部结构时,可以使用迭代器模式。
如果同一个聚合对象有多种遍历方式(如顺序遍历、逆序遍历等),可以使用多个具体迭代器类来实现不同的遍历算法。
迭代器模式允许你使用相同的代码来遍历不同的聚合结构(如列表、树等),从而简化了客户端代码。
4.3 代码示例
// 迭代器接口
interface Iterator {
boolean hasNext();
Object next();
}
// 聚合接口
interface Aggregate {
Iterator createIterator();
}
// 具体聚合类
class ConcreteAggregate implements Aggregate {
private List<String> items = new ArrayList<>();
public void addItem(String item) {
items.add(item);
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(this);
}
// 为了演示,提供一个获取元素的方法(在实际应用中,这个方法可能是私有的)
public List<String> getItems() {
return items;
}
}
// 具体迭代器类
class ConcreteIterator implements Iterator {
private int currentIndex = 0;
private ConcreteAggregate aggregate;
public ConcreteIterator(ConcreteAggregate aggregate) {
this.aggregate = aggregate;
}
@Override
public boolean hasNext() {
return currentIndex < aggregate.getItems().size();
}
@Override
public Object next() {
if (this.hasNext()) {
return aggregate.getItems().get(currentIndex++);
}
return null;
}
}
5. 责任链模式
5.1 简介
创建了一个对象链,并沿着这条链传递请求,直到有一个对象处理它为止。在责任链模式中,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
5.2 使用场景
多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态决定。
多级审批流程(如采购审批、报销流程等)就是一个典型的责任链模式应用场景。每个审批级别都可以看作链上的一个节点,请求(如报销申请)从链的首端发出,依次经过每个审批节点,直到被某个节点处理或拒绝为止。
5.3 代码示例
// 抽象处理者角色(Handler)
public abstract class Handler {
protected Handler successor; // 持有后继者的引用
// 设置后继者
public void setSuccessor(Handler successor) {
this.successor = successor;
}
// 处理请求的方法,具体实现由子类完成
public abstract void handleRequest(String request);
}
// 具体处理者角色(ConcreteHandler)
public class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(String request) {
if (/* 满足某些条件 */) {
// 处理请求
System.out.println("ConcreteHandler1处理请求:" + request);
} else {
// 转发请求给后继者
if (successor != null) {
successor.handleRequest(request);
}
}
}
}
6. 命令模式
6.1 简介
尝试将请求封装成对象,从而让你使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式将“请求”封装成对象,以便使用不同的请求把客户端参数化;命令模式可以很容易地设计一个命令队列;命令模式可以与组合模式结合,将多个命令组合在一起,形成复合命令。
6.2 使用场景
当系统的某项操作具备命令语义,且命令实现不稳定(变化)时,可以通过命令模式解耦请求与实现。
当请求调用者需要与请求接收者解耦时,命令模式可以使调用者和接收者不直接交互。
系统随机请求命令或经常增加、删除命令时,命令模式可以方便地实现这些功能。
当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
6.3 代码示例
// 命令接口
public interface Command {
void execute();
void undo(); // 可选,用于实现撤销操作
}
// 具体命令类
public class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
@Override
public void undo() {
receiver.undoAction(); // 假设Receiver提供了撤销操作的方法
}
}
// 接收者
public class Receiver {
public void action() {
System.out.println("执行操作");
}
public void undoAction() {
System.out.println("撤销操作");
}
}
// 调用者
public class Invoker {
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
public void undoCommand() {
command.undo();
}
}
以上介绍了行为型模式中的策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式,下一篇我们将继续介绍备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。