1.基本概述
行为型模式关注对象之间的通信和协作方式,即对象之间如何相互交互以完成特定的任务。
用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。
2.模板方法模式
模板方法模式是一种行为型设计模式,它定义了一个操作中的算法的骨架,将一些步骤的具体实现延迟到子类中。通过模板方法模式,可以在不改变算法结构的情况下,重新定义算法的某些步骤。
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
结构
- 抽象类(Abstract Class):定义了一个模板方法,其中包含了算法的骨架,同时定义了一些抽象方法,这些抽象方法由子类来实现具体的步骤。
- 具体子类(Concrete Class):实现了抽象类中定义的抽象方法,以完成算法的具体步骤。
上面的抽象类又由一个模板方法和若干个基本方法构成。
而其中基本方法又可以分为三种:
- 具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
- 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
如下:
package com.dreams.pattern.template;
// 抽象类:烹饪方法
abstract class CookingMethod {
// 模板方法
public final void cook() {
prepareIngredients();
cookFood();
plate();
}
// 抽象方法:准备食材
protected abstract void prepareIngredients();
// 抽象方法:烹饪食物
protected abstract void cookFood();
// 具体方法:装盘
protected void plate() {
System.out.println("Plate the dish.");
}
}
// 具体子类:炒菜
class StirFry extends CookingMethod {
@Override
protected void prepareIngredients() {
System.out.println("Prepare vegetables and meat.");
}
@Override
protected void cookFood() {
System.out.println("Stir fry the ingredients.");
}
}
// 具体子类:煮菜
class Boil extends CookingMethod {
@Override
protected void prepareIngredients() {
System.out.println("Prepare vegetables and broth.");
}
@Override
protected void cookFood() {
System.out.println("Boil the ingredients.");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
CookingMethod stirFry = new StirFry();
stirFry.cook(); // Output: Prepare vegetables and meat. Stir fry the ingredients. Plate the dish.
CookingMethod boil = new Boil();
boil.cook(); // Output: Prepare vegetables and broth. Boil the ingredients. Plate the dish.
}
}CookingMethod 是抽象类,定义了烹饪的模板方法 cook(),以及准备食材和烹饪食物的抽象方法。具体的烹饪方法由 StirFry 和 Boil 这两个具体子类来实现。通过调用 cook() 方法,可以按照模板方法中定义的步骤完成烹饪过程。
JDK源码解析
InputStream类就使用了模板方法模式。在InputStream类中定义了多个 read()方法
无参的read()方法是抽象方法,要求子类必须实现。
read(byte b[])方法调用了read(byte b[], int off, int len)方法,里面调用了无参的抽象的 read() 方法
在InputStream父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,并将其存储到数组的第一个索引位置,读取len个字节数据。由子类实现。
3.策略模式
策略模式是一种行为型设计模式,它定义了一系列算法,将每个算法封装到具有共同接口的独立的类中,并使它们可以相互替换。通过策略模式,客户端可以在运行时根据需要选择算法,而不必修改具体的使用算法的代码。
结构:
- 环境类(Context):持有一个策略对象的引用,可以在运行时切换不同的策略。最终给客户端调用。
- 策略接口(Strategy Interface):定义了所有支持的算法的通用接口,通常由抽象类或接口实现。
- 具体策略类(Concrete Strategies):实现了策略接口,封装了具体的算法。
代码:
package com.dreams.pattern.strategy;
// 策略接口:排序策略
interface SortingStrategy {
void sort(int[] arr);
}
// 具体策略类:冒泡排序
class BubbleSort implements SortingStrategy {
@Override
public void sort(int[] arr) {
// 冒泡排序算法实现
System.out.println("Sorting using Bubble Sort.");
}
}
// 具体策略类:快速排序
class QuickSort implements SortingStrategy {
@Override
public void sort(int[] arr) {
// 快速排序算法实现
System.out.println("Sorting using Quick Sort.");
}
}
// 环境类:排序器
class Sorter {
private SortingStrategy strategy;
public Sorter(SortingStrategy strategy) {
this.strategy = strategy;
}
// 设置策略
public void setStrategy(SortingStrategy strategy) {
this.strategy = strategy;
}
// 执行排序
public void performSort(int[] arr) {
strategy.sort(arr);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
int[] arr = {5, 2, 7, 3, 1};
Sorter sorter = new Sorter(new BubbleSort());
sorter.performSort(arr); // Output: Sorting using Bubble Sort.
sorter.setStrategy(new QuickSort());
sorter.performSort(arr); // Output: Sorting using Quick Sort.
}
}SortingStrategy 是策略接口,定义了排序算法的通用接口。BubbleSort 和 QuickSort 是具体的策略类,分别实现了冒泡排序和快速排序算法。Sorter 是环境类,持有一个策略对象的引用,可以在运行时切换不同的排序策略。通过调用 performSort() 方法,可以执行具体的排序算法。
增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“
JDK源码解析:
Comparator中的策略模式。在Arrays类中有一个sort()方法,Arrays就是一个环境角色类,这个sort方法可以传一个新策略让Arrays根据这个策略来进行排序。调用Arrays的sort方法时,第二个参数传递的是Comparator接口的子实现类对象。所以Comparator充当的是抽象策略角色,而具体的子实现类充当的是具体策略角色。环境角色类(Arrays)应该持有抽象策略的引用来调用。
4.命令模式
介绍
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。
结构角色
- 命令接口(Command Interface):声明了执行命令的方法,通常包括一个execute() 方法。
- 具体命令类(Concrete Commands):实现了命令接口,负责执行具体的操作,包括接收请求者并将请求转换成对应的动作或行为。通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
- 调用/请求者(Invoker):负责调用命令对象执行请求的对象。通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
- 实现/接收者(Receiver):负责实施命令指定的操作,实际执行请求的对象。
代码:
package com.dreams.pattern.command;
// 定义命令接口
interface Command {
void execute();
}
// 具体命令类:开灯命令
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}
// 具体命令类:关灯命令
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOff();
}
}
// 接收者类:灯
class Light {
public void turnOn() {
System.out.println("灯已打开");
}
public void turnOff() {
System.out.println("灯已关闭");
}
}
// 请求者类:遥控器
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建接收者对象
Light light = new Light();
// 创建具体命令对象并设置接收者
Command turnOnCommand = new LightOnCommand(light);
Command turnOffCommand = new LightOffCommand(light);
// 创建请求者对象并设置命令
RemoteControl remoteControl = new RemoteControl();
remoteControl.setCommand(turnOnCommand);
// 发送请求
remoteControl.pressButton(); // Output: 灯已打开
// 设置不同的命令
remoteControl.setCommand(turnOffCommand);
// 发送请求
remoteControl.pressButton(); // Output: 灯已关闭
}
}遥控器(RemoteControl)充当了请求者,通过设置不同的命令对象来发送请求。命令对象(例如 TurnOnTVCommand 和 TurnOffTVCommand)封装了对电视(TV)进行开启和关闭操作的请求。这种设计使得遥控器和电视之间的耦合度降低,可以轻松地添加新的命令和接收者而不需要修改现有代码。
优点:
- 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
- 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
- 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
5.责任链模式
介绍
责任链模式是一种行为型设计模式,用于将请求的发送者和接收者解耦,并且允许多个对象都有机会处理请求。在责任链模式中,请求沿着链传递,直到有一个对象处理它为止。
结构:
- 处理者接口(Handler):声明处理请求的方法,并且可以定义一个链中的下一个处理者。
- 具体处理者类(Concrete Handler):实现处理者接口,在接收到请求时,可以选择将请求处理掉或者将请求传递给下一个处理者。
- 客户端(Client):创建处理者对象并组成责任链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
示例代码:
购买审批系统,每个级别的审批人员有不同的审批权限。如果购买金额在一定范围内,由对应级别的审批人员审批通过;否则,交由下一级别的审批人员处理,直到最终得到审批。
package com.dreams.pattern.responsibility;
// 处理者接口
interface Approver {
void approvePurchase(Purchase purchase);
void setNextApprover(Approver next);
}
// 具体处理者类:经理
class Manager implements Approver {
private Approver nextApprover;
public void setNextApprover(Approver next) {
this.nextApprover = next;
}
public void approvePurchase(Purchase purchase) {
if (purchase.getAmount() <= 1000) {
System.out.println("经理审批通过:" + purchase);
System.out.println();
} else if (nextApprover != null) {
System.out.println("当前等级经理无法审批,交给下一等级审批");
nextApprover.approvePurchase(purchase);
} else {
System.out.println("无法审批:" + purchase);
}
}
}
// 具体处理者类:副总裁
class VicePresident implements Approver {
private Approver nextApprover;
public void setNextApprover(Approver next) {
this.nextApprover = next;
}
public void approvePurchase(Purchase purchase) {
if (purchase.getAmount() <= 5000) {
System.out.println("副总裁审批通过:" + purchase);
System.out.println();
} else if (nextApprover != null) {
System.out.println("当前等级副总裁无法审批,交给下一等级审批");
nextApprover.approvePurchase(purchase);
} else {
System.out.println("无法审批:" + purchase);
}
}
}
// 具体处理者类:总裁
class President implements Approver {
public void approvePurchase(Purchase purchase) {
System.out.println("总裁审批通过:" + purchase);
System.out.println();
}
public void setNextApprover(Approver next) {
// 总裁是最终审批者,不设置下一个处理者
}
}
// 请求类:购买
class Purchase {
private String item;
private double amount;
public Purchase(String item, double amount) {
this.item = item;
this.amount = amount;
}
public String getItem() {
return item;
}
public double getAmount() {
return amount;
}
@Override
public String toString() {
return "Purchase [item=" + item + ", amount=" + amount + "]";
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建具体处理者对象
Approver manager = new Manager();
Approver vicePresident = new VicePresident();
Approver president = new President();
// 设置处理链
manager.setNextApprover(vicePresident);
vicePresident.setNextApprover(president);
// 创建请求对象
Purchase purchase1 = new Purchase("Office Supplies", 500);
Purchase purchase2 = new Purchase("New Laptop", 2500);
Purchase purchase3 = new Purchase("Company Car", 10000);
// 发送请求
manager.approvePurchase(purchase1);
manager.approvePurchase(purchase2);
manager.approvePurchase(purchase3);
}
}
购买审批系统中的每个处理者根据自己的审批权限来处理请求,如果自己无法处理,则将请求传递给下一个处理者,直到有一个处理者处理该请求或者到达处理链的末端。通过这种方式,系统可以动态地调整审批流程,适应不同情况下的审批需求。
总结
- 降低了请求发送者和接收者的耦合度。
- 增强了系统的可扩展性
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
责任链
6.状态模式
状态模式是一种行为型设计模式,用于实现对象在不同状态下的行为变化。在状态模式中,对象的行为会根据其内部状态的改变而改变,使得对象看起来好像修改了它的类。
这种模式的核心思想是将对象的各种状态封装成独立的类,并且使得对象在不同状态下有不同的行为。这样做的好处是将状态转换的逻辑分布到状态类中,使得状态转换更加简单、清晰,并且符合开闭原则。
状态模式的结构:
- Context(环境):定义客户需要的接口,并且保留一个具体状态类的实例,维护对象当前状态。
- State(状态抽象类/接口):定义一个接口,用以封装环境对象中的特定状态所对应的行为。
- ConcreteState(具体状态类):每个具体状态类实现 State 接口,定义对象在该状态下的具体行为。
代码:
package com.dreams.pattern.state;
// 状态接口
interface State {
void handle(Context context);
}
// 具体状态类:打开状态
class OpenState implements State {
public void handle(Context context) {
System.out.println("电梯打开");
// 其他处理逻辑
}
}
// 具体状态类:关闭状态
class CloseState implements State {
public void handle(Context context) {
System.out.println("电梯关闭");
// 其他处理逻辑
}
}
// 环境类
class Context {
private State state; // 当前状态
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this); // 由具体状态类处理请求
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建环境对象
Context context = new Context();
// 设置初始状态
context.setState(new CloseState());
// 发送请求
context.request(); // 电梯关闭
context.setState(new OpenState());
// 发送请求
context.request(); // 电梯打开
}
}
7.观察者模式
介绍
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。又被称为发布-订阅(Publish/Subscribe)模式
- Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
代码:
package com.dreams.pattern.observer;
import java.util.ArrayList;
import java.util.List;
// 观察者接口
interface Observer {
void update(String message);
}
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题类
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String message;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(message);
}
}
public void setMessage(String message) {
this.message = message;
notifyObservers();
}
}
// 具体观察者类
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 ObserverPatternExample {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver("Observer 1");
Observer observer2 = new ConcreteObserver("Observer 2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.setMessage("Hello World!");
// Output:
// Observer 1 received message: Hello World!
// Observer 2 received message: Hello World!
subject.removeObserver(observer1);
subject.setMessage("How are you?");
// Output:
// Observer 2 received message: How are you?
}
}
这是一个简单的消息发布者,观察者是接收消息并做出响应的订阅者。通过观察者模式,实现了发布者和订阅者之间的解耦,让系统更加灵活和可扩展。
在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
Observable类
Observable 类是抽象目标类(被观察者),它有一个 Vector 集合成员变量,用于保存所有要通知的观察者对象。
它最重要的 3 个方法。
void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。
Observer 接口
8.中介者模式
中介者模式是一种行为设计模式,旨在通过将对象间的通信集中在一个中介对象中,来减少对象之间的直接耦合。在中介者模式中,对象之间不再直接相互引用,而是通过中介者对象进行通信。
这种模式适用于当对象之间的交互变得复杂且难以维护时,可以引入一个中介者对象来管理对象之间的通信,从而降低系统的复杂性。中介者模式通常包括以下几个角色:
- 中介者(Mediator):定义了对象间通信的接口,通常包括注册、发送消息等方法。中介者对象通常维护一个对所有对象的引用,并协调它们之间的交互。
- 具体中介者(Concrete Mediator):实现了中介者接口,负责实际的通信逻辑。它了解各个同事对象,并负责协调它们之间的交互。
- 同事(Colleague):是指对象间需要进行通信的各个参与者。保存中介者对象,同事对象通过中介者来完成与其他同事的通信,而不是直接与其他同事进行交互。
- 具体同事(Concrete Colleague):实现了同事接口,负责自身的行为,并通过中介者来与其他同事进行通信。
代码:
package com.dreams.pattern.mediator;
import java.util.ArrayList;
import java.util.List;
// 中介者接口
interface Mediator {
void sendMessage(Colleague colleague, String message);
}
// 具体中介者:房屋中介公司
class HouseMediator implements Mediator {
private List<Colleague> colleagues;
public HouseMediator() {
this.colleagues = new ArrayList<>();
}
public void addColleague(Colleague colleague) {
this.colleagues.add(colleague);
}
@Override
public void sendMessage(Colleague colleague, String message) {
for (Colleague col : colleagues) {
// 不发送消息给发送消息的同事
if (col != colleague) {
col.receiveMessage(message);
}
}
}
}
// 同事接口
interface Colleague {
void sendMessage(String message);
void receiveMessage(String message);
}
// 具体同事类:房东
class Landlord implements Colleague {
private Mediator mediator;
public Landlord(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(this, message);
}
@Override
public void receiveMessage(String message) {
System.out.println("Landlord received message: " + message);
}
}
// 具体同事类:租客
class Tenant implements Colleague {
private Mediator mediator;
public Tenant(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(this, message);
}
@Override
public void receiveMessage(String message) {
System.out.println("Tenant received message: " + message);
}
}
public class MediatorPatternHouseExample {
public static void main(String[] args) {
// 创建房屋中介
HouseMediator mediator = new HouseMediator();
// 创建具体同事:房东和租客
Colleague landlord = new Landlord(mediator);
Colleague tenant = new Tenant(mediator);
// 中介者添加同事
mediator.addColleague(landlord);
mediator.addColleague(tenant);
// 房东和租客之间发送消息
landlord.sendMessage("I have a house for rent.");
tenant.sendMessage("I'm interested in renting a house.");
}
}
房东和租客通过房屋中介公司进行通信,而不直接联系。房东和租客都实现了 Colleague 接口,并在其中调用了中介者的 sendMessage 方法来发送消息。中介者负责接收并转发消息给其他同事。
9.迭代器模式
迭代器模式是一种行为设计模式,用于提供一种方法来顺序访问一个聚合对象中的各个元素,而又不暴露其内部表示。这种模式非常有用,因为它允许你在不暴露聚合对象的内部结构的情况下遍历聚合对象。
- 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
- 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
- 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。
代码:
package com.dreams.pattern.iterator;
import java.util.Arrays;
// 迭代器接口
interface MyIterator {
boolean hasNext();
Object next();
}
// 集合类接口
interface MyCollection {
MyIterator createIterator();
}
// 具体迭代器类
class ConcreteIterator implements MyIterator {
private Object[] items;
private int position = 0;
public ConcreteIterator(Object[] items) {
this.items = items;
}
@Override
public boolean hasNext() {
return position < items.length;
}
@Override
public Object next() {
Object item = items[position];
position++;
return item;
}
}
// 具体集合类
class MyList implements MyCollection {
private Object[] items;
public MyList(Object[] items) {
this.items = items;
}
@Override
public MyIterator createIterator() {
return new ConcreteIterator(items);
}
}
public class IteratorPatternExample {
public static void main(String[] args) {
// 创建集合对象
Object[] items = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"};
MyCollection collection = new MyList(items);
// 创建迭代器
MyIterator iterator = collection.createIterator();
// 使用迭代器遍历集合并输出元素
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
10.访问者模式
访问者模式是一种行为设计模式,它允许你在不修改已有代码的情况下,向现有类层次结构中添加新的行为。这种模式非常适用于对象结构比较稳定,但其操作算法经常需要变更或者增加的情况。
- 抽象访问者(Visitor)角色:定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变。
- 具体访问者(ConcreteVisitor)角色:给出对每一个元素类访问时所产生的具体行为。
- 抽象元素(Element)角色:定义了一个接受访问者的方法(accept),其意义是指,每一个元素都要可以被访问者访问。
- 具体元素(ConcreteElement)角色: 提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
- 对象结构(Object Structure)角色:定义当中所提到的对象结构,对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,
代码:
package com.dreams.pattern.visitor;
// 访问者接口
interface AnimalVisitor {
void visit(Cat cat);
void visit(Dog dog);
}
// 具体访问者类:实现不同操作
class AnimalCareVisitor implements AnimalVisitor {
@Override
public void visit(Cat cat) {
System.out.println("Feeding cat...");
}
@Override
public void visit(Dog dog) {
System.out.println("Feeding dog...");
}
}
// 具体访问者类:实现不同操作
class AnimalGroomVisitor implements AnimalVisitor {
@Override
public void visit(Cat cat) {
System.out.println("Bathing cat...");
}
@Override
public void visit(Dog dog) {
System.out.println("Bathing dog...");
}
}
// 抽象元素类
interface Animal {
void accept(AnimalVisitor visitor);
}
// 具体元素类:猫
class Cat implements Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visit(this);
}
}
// 具体元素类:狗
class Dog implements Animal {
@Override
public void accept(AnimalVisitor visitor) {
visitor.visit(this);
}
}
public class VisitorPatternExample {
public static void main(String[] args) {
// 创建具体访问者
AnimalVisitor careVisitor = new AnimalCareVisitor();
AnimalVisitor groomVisitor = new AnimalGroomVisitor();
// 创建具体元素
Animal cat = new Cat();
Animal dog = new Dog();
// 元素接受访问者操作
cat.accept(careVisitor);
dog.accept(careVisitor);
cat.accept(groomVisitor);
dog.accept(groomVisitor);
}
}Animal 接口表示元素类,Cat 和 Dog 是具体的元素类。AnimalVisitor 接口表示访问者类,AnimalCareVisitor 和 AnimalGroomVisitor 是具体的访问者类,分别实现了喂食和洗澡两种不同的操作。元素类通过 accept 方法接受访问者对象,从而实现了在不修改元素类的情况下,为元素类添加新的操作。
通过使用访问者模式,我们可以很容易地为现有的类层次结构添加新的操作,而无需修改这些类的代码。
总结
- 扩展性好,在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
- 复用性好,通过访问者来定义整个对象结构通用的功能,从而提高复用程度。
- 分离无关行为
- 通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。
2.缺点:
- 对象结构变化很困难,在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
- 违反了依赖倒置原则,
11.备忘录模式
备忘录模式是一种行为设计模式,它允许你捕获一个对象的内部状态,并在需要时将其恢复到之前的状态,而不暴露其实现细节。这种模式常常用于需要保存和恢复对象状态的情况,比如说撤销操作。
- 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
- 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
- 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
备忘录有两个等效的接口:
窄接口:管理者(Caretaker)对象(和其他发起人对象之外的任何对象)看到的是备忘录的窄接口(narror Interface),这个窄接口只允许他把备忘录对象传给其他的对象。
宽接口
白箱备忘录
白箱备忘录是备忘录模式的一种形式,它将备忘录的状态信息完全暴露给发起者(Originator),即发起者可以直接访问备忘录中的状态信息。
在白箱备忘录中,备忘录类通常包含了与发起者相关的状态信息,并且提供了公共的访问方法供发起者访问。这样做的好处是备忘录的实现非常简单,但同时也带来了一些风险,因为发起者可以直接修改备忘录中的状态信息,从而可能导致备忘录模式失去原本的用意。
package com.dreams.pattern.memento.white_box;
// 备忘录类:白箱备忘录
class WhiteBoxMemento {
private String state;
public WhiteBoxMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
// 发起者类
class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
// 创建备忘录
public WhiteBoxMemento save() {
return new WhiteBoxMemento(state);
}
// 恢复状态
public void restore(WhiteBoxMemento memento) {
this.state = memento.getState();
}
}
public class WhiteBoxMementoPatternExample {
public static void main(String[] args) {
// 创建发起者
Originator originator = new Originator();
// 设置状态
originator.setState("State 1");
// 创建备忘录并保存状态
WhiteBoxMemento memento = originator.save();
// 修改状态
originator.setState("State 2");
// 恢复到之前状态
originator.restore(memento);
// 输出当前状态
System.out.println(originator.getState()); // 输出:State 1
}
}
黑箱备忘录
在黑箱备忘录模式中,备忘录类是私有的,外部无法直接访问其状态。这样可以确保发起者类可以自由地保存和恢复状态,而无需外部知道备忘录的具体实现。以下是将备忘录类设计为发起者类的内部成员类的示例代码
package com.dreams.pattern.memento.black_box;
// 备忘录接口
interface BlackBoxMemento {
}
// 发起者类
class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
// 内部备忘录类
private class Memento implements BlackBoxMemento {
private final String state;
private Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 创建备忘录
public BlackBoxMemento save() {
return new Memento(state);
}
// 恢复状态
public void restore(BlackBoxMemento memento) {
if (memento instanceof Memento) {
Memento internalMemento = (Memento) memento;
this.state = internalMemento.getState();
} else {
throw new IllegalArgumentException("Invalid memento type");
}
}
}
// 管理者类
class Caretaker {
private BlackBoxMemento memento;
public BlackBoxMemento getMemento() {
return memento;
}
public void setMemento(BlackBoxMemento memento) {
this.memento = memento;
}
}
public class BlackBoxMementoPatternExample {
public static void main(String[] args) {
// 创建发起者
Originator originator = new Originator();
// 创建管理者
Caretaker caretaker = new Caretaker();
// 设置状态并保存备忘录
originator.setState("State 1");
caretaker.setMemento(originator.save());
// 修改状态
originator.setState("State 2");
// 恢复到之前状态
originator.restore(caretaker.getMemento());
// 输出恢复后的状态
System.out.println("恢复后的状态:" + originator.getState());
}
}
12.解释器模式
解释器模式是一种行为设计模式,用于解释语言语法或表达式的模式。它主要用于创建一个解释器,以解释预定义语言的语法或表达式,并执行相应的操作。这个模式通常适用于那些需要处理和解释类似语言的问题,例如编程语言解释器、正则表达式引擎等。
解释器模式包含以下主要组件:
- 抽象表达式(Abstract Expression):定义解释器的接口,通常包含一个 interpret() 方法,该方法根据具体的解释逻辑对表达式进行解释。
- 终结符表达式(Terminal Expression):实现抽象表达式接口的类,用于表示语法规则中的终结符。
- 非终结符表达式(Non-terminal Expression):也是实现抽象表达式接口的类,用于表示语法规则中的非终结符,通常包含其他表达式作为子表达式。
- 上下文(Context):包含解释器解释的上下文信息,可以包括解释器所需的全局信息或环境信息。
- 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
一个简单的算术表达式解释器,可以解释类似 “2 + 3 – 1” 的表达式。我们可以使用解释器模式来实现这个解释器。
代码:
package com.dreams.pattern.interpreter;
// 抽象表达式接口
interface Expression {
int interpret(Context context);
}
// 终结符表达式:数字表达式
class NumberExpression implements Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret(Context context) {
return number;
}
}
// 非终结符表达式:加法表达式
class AddExpression implements Expression {
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
}
// 上下文类
class Context {
// 可以在这里添加上下文信息
}
public class InterpreterPatternExample {
public static void main(String[] args) {
// 构建解释器上下文
Context context = new Context();
// 构建表达式:2 + 3 - 1
Expression expression = new AddExpression(
new NumberExpression(2),
new AddExpression(
new NumberExpression(3),
new NumberExpression(-1)
)
);
// 解释表达式并输出结果
int result = expression.interpret(context);
System.out.println("解释结果:" + result); // 输出:4
}
}
参考
大话设计模式
图解设计模式


