Java软件设计中的设计模式主要分为三大类:创建型模式、结构型模式和行为型模式。这些模式为解决常见设计问题提供了通用的解决方案,提升代码的可复用性、可维护性和可扩展性。
创建型模式
创建型模式(Creational Patterns)主要处理对象创建的方式,目的是将对象的创建与使用分离,并避免硬编码构造过程。通过使用创建型模式,可以提高代码的灵活性、可扩展性和可维护性。以下是五种常见的创建型模式的详细说明。
1. 单例模式(Singleton Pattern)
定义
确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
动机
在某些情况下,整个应用程序中只需要一个类的实例,如日志管理器、数据库连接池、配置管理等。通过单例模式可以确保只有一个实例被创建,避免重复创建带来的资源浪费,并且全局访问点确保所有客户端都能访问到同一个实例。
结构
- Singleton类:持有自己的静态实例,并提供一个静态方法来返回这个唯一实例。
- 构造方法:私有,防止外部类直接实例化对象。
示例
public class Singleton {
// 私有的静态实例
private static Singleton instance;
// 私有构造方法,防止外部实例化
private Singleton() {}
// 静态方法,提供全局访问点
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
应用场景
- 日志记录器
- 数据库连接池
- 配置管理器
优点
- 控制实例数量,节省资源
- 提供全局访问点
缺点
- 不支持多线程(如果不进行同步处理,可能会导致多个实例的创建)
- 单例对象难以扩展和测试(例如,在单元测试中需要使用多个不同实例时可能会有困难)
多线程改进(双重检查锁定)
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2. 工厂方法模式(Factory Method Pattern)
定义
定义一个用于创建对象的接口,但由子类决定具体要实例化的类。工厂方法让一个类的实例化延迟到子类。
动机
当代码中需要创建对象,但不想暴露具体类的构造过程,或者为了扩展方便,希望可以灵活地指定子类来实例化具体对象时,工厂方法模式提供了解决方案。
结构
- 抽象产品类:定义产品的接口。
- 具体产品类:实现抽象产品的接口,表示不同类型的产品。
- 抽象工厂类:定义一个抽象的工厂方法,返回抽象产品类型。
- 具体工厂类:实现工厂方法,返回具体的产品实例。
示例
// 产品接口
abstract class Product {
abstract void use();
}
// 具体产品类
class ConcreteProduct extends Product {
void use() {
System.out.println("Using ConcreteProduct");
}
}
// 工厂接口
abstract class Creator {
abstract Product createProduct();
}
// 具体工厂类
class ConcreteCreator extends Creator {
Product createProduct() {
return new ConcreteProduct();
}
}
应用场景
- 日志记录系统:根据不同的日志级别创建不同的日志处理器。
- 数据库访问层:使用不同的数据库(如MySQL、Oracle等)时,通过工厂方法灵活选择数据库驱动。
优点
- 客户端不需要知道具体产品的创建逻辑,只依赖工厂接口。
- 符合“开放-封闭”原则:可以通过子类扩展新产品,而无需修改现有代码。
缺点
- 增加了系统的复杂度,需要创建额外的工厂类。
3. 抽象工厂模式(Abstract Factory Pattern)
定义
提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。
动机
如果系统需要多个相互关联的对象,且不希望在客户端直接依赖具体类时,抽象工厂模式通过提供统一的接口,来生成相关的对象族,从而保证了对象之间的一致性和可扩展性。
结构
- 抽象工厂接口:定义创建一系列相关产品的接口。
- 具体工厂类:实现抽象工厂接口,负责生成具体的产品。
- 抽象产品接口:定义产品的通用接口。
- 具体产品类:实现抽象产品接口,表示不同的产品。
示例
// 抽象产品
interface Button {
void paint();
}
interface Checkbox {
void paint();
}
// 具体产品
class WinButton implements Button {
public void paint() {
System.out.println("Render a button in Windows style.");
}
}
class MacButton implements Button {
public void paint() {
System.out.println("Render a button in MacOS style.");
}
}
// 抽象工厂
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// 具体工厂
class WinFactory implements GUIFactory {
public Button createButton() {
return new WinButton();
}
public Checkbox createCheckbox() {
return new WinCheckbox();
}
}
class MacFactory implements GUIFactory {
public Button createButton() {
return new MacButton();
}
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}
应用场景
- 跨平台的GUI工具包:为不同操作系统(如Windows、Mac)创建风格一致的UI组件。
- 数据库访问:为不同数据库(如MySQL、PostgreSQL)生成一组相关的操作对象。
优点
- 客户端不需要直接实例化对象,易于扩展。
- 保证了相关产品之间的一致性。
缺点
- 代码复杂度增加,需要维护更多的类。
- 当需要扩展新产品族时,可能需要修改抽象工厂接口。
4. 建造者模式(Builder Pattern)
定义
将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示。
动机
有些对象的构建过程非常复杂,包含多个步骤(如配置文件的解析、复杂UI界面的生成)。建造者模式允许分步骤创建对象,且同样的构造过程可以构建不同的对象。
结构
- Builder接口:定义构建复杂对象的步骤。
- ConcreteBuilder类:实现Builder接口,完成具体步骤。
- Director类:负责按顺序调用建造者的构建步骤。
- Product类:复杂对象,由多个部分组成。
示例
// 产品类
class Product {
private String partA;
private String partB;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
public void showProduct() {
System.out.println("Product with " + partA + " and " + partB);
}
}
// 抽象建造者
interface Builder {
void buildPartA();
void buildPartB();
Product getResult();
}
// 具体建造者
class ConcreteBuilder implements Builder {
private Product product = new Product();
public void buildPartA() {
product.setPartA("Part A");
}
public void buildPartB() {
product.setPartB("Part B");
}
public Product getResult() {
return product;
}
}
// 指挥者
class Director {
public void construct(Builder builder) {
builder.buildPartA();
builder.buildPartB();
}
}
应用场景
- 复杂对象的创建,如车辆、房屋、文档生成器。
- 当构造过程需要按照一定的顺序时,如餐厅订单的构建(前菜、主菜、甜点)。
优点
- 将构造过程分离,允许同样的构造过程创建不同的对象。
- 更加灵活,构造过程更容易扩展和维护。
缺点
- 增加了代码的复杂性,尤其是当产品本身并不复杂时。
5. 原型模式(Prototype Pattern)
定义
通过复制现有对象来创建新对象,而不是通过实例化类。
动机
有时创建对象的成本很高或者很复杂(如加载大量数据或依赖外部资源),直接克隆现有对象可以有效提升性能,避免冗余计算或复杂的初始化过程。
结构
- Prototype接口:定义一个
clone()方法,用于复制对象。 - ConcretePrototype类:实现Prototype接口,负责具体的克隆操作。
示例
// 原型接口
interface Prototype {
Prototype clone();
}
// 具体原型
class ConcretePrototype implements Prototype {
private String state;
public ConcretePrototype(String state) {
this.state = state;
}
@Override
public Prototype clone() {
return new ConcretePrototype(state);
}
}
结构型模式
结构型模式是设计模式中的一种类型,关注于如何将类或对象组合成更大的结构,以形成更复杂的功能。结构型模式帮助我们简化设计、提高系统的灵活性和可复用性,并有助于在不同的类之间建立良好的关系。
以下是几种常见的结构型模式的详细说明:
1. 适配器模式(Adapter Pattern)
定义
适配器模式允许将一个类的接口转换成客户端所期望的另一种接口,使得原本因接口不兼容而无法一起工作的类可以一起工作。
动机
在系统需要与多个接口不兼容的类交互时,适配器模式提供了一种解决方案。
实现
适配器可以是类适配器或对象适配器。类适配器通过继承来实现,而对象适配器通过组合来实现。
示例
// 目标接口
interface Target {
void request();
}
// 适配者类
class Adaptee {
void specificRequest() {
System.out.println("Called specificRequest");
}
}
// 适配器类
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
优点
- 使得接口不兼容的类可以协同工作。
- 提高系统的可复用性和灵活性。
缺点
- 可能会增加系统的复杂性,过多的适配器会导致代码难以理解。
2. 桥接模式(Bridge Pattern)
定义
桥接模式通过将抽象部分与其实现部分分离,从而使它们可以独立变化。即在抽象类和具体实现之间引入一个桥接接口。
动机
当一个类的抽象和实现之间有多个变化维度时,桥接模式能够将这些变化分离,减少系统的复杂性。
结构
- 抽象类:定义了高层接口。
- 实现类:实现了低层接口。
- 桥接:将抽象类与实现类连接。
示例
// 实现接口
interface Implementor {
void operationImpl();
}
// 具体实现
class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorA operation");
}
}
// 抽象类
abstract class Abstraction {
protected Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
// 具体抽象类
class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
implementor.operationImpl();
}
}
优点
- 通过分离抽象和实现,提高了系统的灵活性。
- 可以在不改变抽象和实现的情况下扩展系统。
缺点
- 可能会增加系统的复杂性,特别是在类的数量较多时。
3. 组合模式(Composite Pattern)
定义
组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式允许客户端以统一的方式对待单个对象和组合对象。
动机
当需要表示对象的部分和整体的关系时,组合模式提供了一种简单的方法来管理这些对象。
结构
- 组件:定义了叶子和组合对象的公共接口。
- 叶子:实现了组件接口,表示树的叶子节点。
- 组合:实现了组件接口,表示树的组合节点。
示例
// 组件接口
interface Component {
void operation();
}
// 叶子类
class Leaf implements Component {
@Override
public void operation() {
System.out.println("Leaf operation");
}
}
// 组合类
class Composite implements Component {
private List<Component> children = new ArrayList<>();
public void add(Component component) {
children.add(component);
}
@Override
public void operation() {
for (Component child : children) {
child.operation();
}
}
}
优点
- 客户端可以一致地使用单个对象和组合对象。
- 可以方便地添加新的叶子或组合。
缺点
- 设计上比较复杂,可能会引入不必要的复杂性。
4. 装饰模式(Decorator Pattern)
定义
装饰模式允许在不改变对象自身的情况下,动态地给一个对象添加一些额外的职责。装饰模式提供了比子类更灵活的替代方案。
动机
当需要在运行时扩展对象的功能时,装饰模式提供了一种灵活的方法。
结构
- 组件接口:定义了对象的接口。
- 具体组件:实现了组件接口的基本对象。
- 装饰者抽象类:实现了组件接口,持有一个组件的引用。
- 具体装饰者:扩展了装饰者抽象类,添加额外的职责。
示例
// 组件接口
interface Component {
void operation();
}
// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
// 装饰者抽象类
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰者
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("ConcreteDecoratorA operation");
}
}
优点
- 可以动态地添加职责,增加灵活性。
- 可以通过多个装饰者组合来扩展功能。
缺点
- 可能会导致系统中出现大量小的类,增加管理的复杂性。
5. 代理模式(Proxy Pattern)
定义
代理模式为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端与真实对象之间起到中介作用。
动机
在某些情况下,需要控制对某个对象的访问,比如延迟加载、安全控制、日志记录等。
结构
- 主题接口:定义了真实对象和代理的公共接口。
- 真实对象:实现了主题接口,表示被代理的对象。
- 代理类:实现了主题接口,持有真实对象的引用并控制对它的访问。
示例
// 主题接口
interface Subject {
void request();
}
// 真实对象
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject request");
}
}
// 代理类
class Proxy implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
realSubject.request();
}
}
优点
- 控制访问真实对象的权限,可以实现懒加载。
- 可以在请求前后添加额外的操作(如日志、安全控制等)。
缺点
- 可能会引入不必要的复杂性,特别是在多层代理的情况下。
行为型模式
行为型模式是设计模式中的一种类型,主要关注对象之间的交互和职责分配。它们帮助管理复杂的控制流和对象之间的关系,以便于创建可扩展和可维护的系统。行为型模式通常关注如何使对象之间的交互更加灵活,并提供一种更好的方法来处理系统中的操作。
以下是几种常见的行为型模式的详细说明:
1. 责任链模式(Chain of Responsibility Pattern)
定义
责任链模式将请求的发送者和接收者解耦,通过一系列处理者对象形成一个链,每个处理者决定将请求处理还是将其传递给下一个处理者。
动机
在系统中,可能会有多个处理者需要处理相同的请求,责任链模式能够避免将请求直接发送给某个具体的处理者。
结构
- 处理者接口:定义处理请求的方法。
- 具体处理者:实现处理者接口,处理特定的请求。
- 责任链:将多个处理者连接成一个链。
示例
// 处理者接口
interface Handler {
void setNext(Handler next);
void handleRequest(String request);
}
// 具体处理者A
class ConcreteHandlerA implements Handler {
private Handler next;
@Override
public void setNext(Handler next) {
this.next = next;
}
@Override
public void handleRequest(String request) {
if (request.equals("A")) {
System.out.println("Handler A handled request");
} else if (next != null) {
next.handleRequest(request);
}
}
}
// 具体处理者B
class ConcreteHandlerB implements Handler {
private Handler next;
@Override
public void setNext(Handler next) {
this.next = next;
}
@Override
public void handleRequest(String request) {
if (request.equals("B")) {
System.out.println("Handler B handled request");
} else if (next != null) {
next.handleRequest(request);
}
}
}
优点
- 降低了请求发送者和处理者之间的耦合度。
- 可以动态增加处理者。
缺点
- 可能会导致请求的处理时间不确定,特别是请求在链中经过多个处理者时。
2. 命令模式(Command Pattern)
定义
命令模式将请求封装为对象,从而使得用户可以将请求参数化、排队或记录请求日志,并支持可撤销的操作。
动机
在需要将操作的调用者与执行者解耦时,命令模式提供了一种灵活的解决方案。
结构
- 命令接口:定义命令的接口。
- 具体命令:实现命令接口,定义执行的操作。
- 调用者:调用命令对象来执行请求。
- 接收者:实际执行请求的对象。
示例
// 命令接口
interface Command {
void execute();
}
// 具体命令
class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
}
// 接收者
class Receiver {
public void action() {
System.out.println("Receiver action");
}
}
// 调用者
class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void invoke() {
command.execute();
}
}
优点
- 通过将请求封装为对象,命令模式实现了请求的参数化。
- 可以支持可撤销的操作。
缺点
- 可能会导致系统中出现大量的命令类,增加了复杂性。
3. 观察者模式(Observer Pattern)
定义
观察者模式定义了一种一对多的依赖关系,使得当一个对象状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
动机
在需要建立对象之间的发布-订阅关系时,观察者模式提供了一种有效的解决方案。
结构
- 主题接口:定义注册和通知观察者的方法。
- 具体主题:实现主题接口,管理观察者并通知它们。
- 观察者接口:定义更新方法。
- 具体观察者:实现观察者接口,以便在主题状态变化时进行更新。
示例
// 主题接口
interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
// 具体主题
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String state;
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
public void setState(String state) {
this.state = state;
notifyObservers();
}
}
// 观察者接口
interface Observer {
void update(String state);
}
// 具体观察者
class ConcreteObserver implements Observer {
@Override
public void update(String state) {
System.out.println("Observer updated with state: " + state);
}
}
优点
- 支持广播通信,减少了对象之间的耦合。
- 可以动态地添加和移除观察者。
缺点
- 可能会造成观察者的过度依赖,导致状态更新过于频繁。
4. 状态模式(State Pattern)
定义
状态模式允许一个对象在其内部状态改变时改变其行为。对象看起来似乎修改了其类。
动机
当一个对象的行为与其状态相关时,状态模式提供了一种有效的方法来管理这些状态和相应的行为。
结构
- 状态接口:定义状态的接口。
- 具体状态:实现状态接口,定义与具体状态相关的行为。
- 上下文:维护一个对某一状态对象的引用,负责状态的切换。
示例
// 状态接口
interface State {
void handle();
}
// 具体状态A
class ConcreteStateA implements State {
@Override
public void handle() {
System.out.println("Handling State A");
}
}
// 具体状态B
class ConcreteStateB implements State {
@Override
public void handle() {
System.out.println("Handling State B");
}
}
// 上下文
class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle();
}
}
优点
- 简化了代码,避免了复杂的条件判断。
- 通过状态的封装,提高了系统的可扩展性。
缺点
- 状态类的数量可能会增加,导致系统的复杂性。
5. 策略模式(Strategy Pattern)
定义
策略模式定义了一系列算法,将每一个算法封装起来,并使它们可以互相替换。策略模式让算法独立于使用它的客户端。
动机
在需要在运行时选择算法或行为时,策略模式提供了一种灵活的解决方案。
结构
- 策略接口:定义了一个算法的接口。
- 具体策略:实现策略接口,封装具体的算法。
- 上下文:维护一个对某一策略对象的引用,负责执行策略。
示例
// 策略接口
interface Strategy {
void execute();
}
// 具体策略A
class ConcreteStrategyA implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy A");
}
}
// 具体策略B
class ConcreteStrategyB implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy B");
}
}
// 上下文
class Context {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
strategy.execute();
}
}
优点
- 提高了系统的灵活性和可扩展性。
- 客户端可以根据需要选择策略。
缺点
- 可能会导致策略类的数量增加。
6. 迭代器模式(Iterator Pattern)
定义
迭代器模式提供一种方法访问一个聚合对象的各个元素,而又不暴露该对象的内部表示。
动机
在需要遍历集合但不希望暴露集合的内部结构时,迭代器模式提供了一种清晰的解决方案。
结构
- 迭代器接口:定义遍历的方法(如
next()、hasNext())。 - 具体迭代器:实现迭代器接口,管理对集合的访问。
- 聚合接口:定义创建迭代器的方法。
- 具体聚合:实现聚合接口,提供存储元素的集合。
示例
import java.util.ArrayList;
import java.util.List;
// 迭代器接口
interface Iterator {
boolean hasNext();
Object next();
}
// 具体迭代器
class ConcreteIterator implements Iterator {
private List<Object> items;
private int position = 0;
public ConcreteIterator(List<Object> items) {
this.items = items;
}
@Override
public boolean hasNext() {
return position < items.size();
}
@Override
public Object next() {
return items.get(position++);
}
}
// 聚合接口
interface Aggregate {
Iterator createIterator();
}
// 具体聚合
class ConcreteAggregate implements Aggregate {
private List<Object> items = new ArrayList<>();
public void addItem(Object item) {
items.add(item);
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(items);
}
}
// 使用示例
public class IteratorPatternExample {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate();
aggregate.addItem("Item 1");
aggregate.addItem("Item 2");
aggregate.addItem("Item 3");
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
优点
- 提供了一种统一的遍历方式,支持多种聚合对象。
- 隐藏了集合的内部实现,使得聚合对象的实现可以独立于迭代器的实现。
缺点
- 可能会增加系统的复杂性,特别是在需要多个迭代器时。
7. 中介者模式(Mediator Pattern)
定义
中介者模式定义一个中介对象来封装一系列对象之间的交互,使得对象之间的交互不直接,减少了对象之间的耦合。
动机
在复杂系统中,多个对象之间的交互可能会导致高耦合性,中介者模式可以降低这种耦合。
结构
- 中介者接口:定义各个同事对象之间的交互方法。
- 具体中介者:实现中介者接口,协调同事对象之间的交互。
- 同事接口:定义同事对象的接口。
- 具体同事:实现同事接口,并通过中介者与其他同事进行交互。
示例
// 中介者接口
interface Mediator {
void notify(Object sender, String event);
}
// 具体中介者
class ConcreteMediator implements Mediator {
private ColleagueA colleagueA;
private ColleagueB colleagueB;
public void setColleagueA(ColleagueA colleagueA) {
this.colleagueA = colleagueA;
}
public void setColleagueB(ColleagueB colleagueB) {
this.colleagueB = colleagueB;
}
@Override
public void notify(Object sender, String event) {
if (sender == colleagueA) {
// 处理ColleagueA的事件
System.out.println("Mediator received event from ColleagueA: " + event);
} else if (sender == colleagueB) {
// 处理ColleagueB的事件
System.out.println("Mediator received event from ColleagueB: " + event);
}
}
}
// 同事接口
interface Colleague {
void setMediator(Mediator mediator);
}
// 具体同事A
class ColleagueA implements Colleague {
private Mediator mediator;
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
public void doSomething() {
System.out.println("ColleagueA does something.");
mediator.notify(this, "Action from ColleagueA");
}
}
// 具体同事B
class ColleagueB implements Colleague {
private Mediator mediator;
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
public void doSomething() {
System.out.println("ColleagueB does something.");
mediator.notify(this, "Action from ColleagueB");
}
}
// 使用示例
public class MediatorPatternExample {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ColleagueA colleagueA = new ColleagueA();
colleagueA.setMediator(mediator);
ColleagueB colleagueB = new ColleagueB();
colleagueB.setMediator(mediator);
mediator.setColleagueA(colleagueA);
mediator.setColleagueB(colleagueB);
colleagueA.doSomething();
colleagueB.doSomething();
}
}
优点
- 减少了对象之间的依赖,提高了系统的灵活性。
- 可以集中处理对象之间的交互逻辑。
缺点
- 可能会导致中介者变得复杂,成为系统的单点故障。
8. 备忘录模式(Memento Pattern)
定义
备忘录模式允许在不暴露对象内部状态的情况下,捕获并外部存储一个对象的内部状态,以便在以后可以恢复到该状态。
动机
在需要保存对象的历史状态以便在需要时恢复时,备忘录模式提供了一种简单的实现。
结构
- 备忘录:用于存储对象的状态。
- 发起人:创建备忘录并负责恢复状态。
- 管理者:管理多个备忘录的存储。
示例
// 备忘录
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 发起人
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
// 管理者
class Caretaker {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento state) {
mementoList.add(state);
}
public Memento get(int index) {
return mementoList.get(index);
}
}
// 使用示例
public class MementoPatternExample {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("State #1");
caretaker.add(originator.saveStateToMemento());
originator.setState("State #2");
caretaker.add(originator.saveStateToMemento());
originator.setState("State #3");
System.out.println("Current State: " + originator.state);
originator.getStateFromMemento(caretaker.get(0));
System.out.println("Restored State: " + originator.state);
}
}
优点
- 可以在不暴露实现细节的情况下恢复对象的状态。
- 提高了对象状态管理的灵活性。
缺点
- 可能会增加系统的内存开销,特别是在需要存储大量状态时。
9. 访问者模式(Visitor Pattern)
定义
访问者模式允许在不改变对象结构的前提下,定义作用于这些对象的新操作。通过将操作封装在访问者中,可以对一组对象进行操作。
动机
在需要对对象结构中的元素执行一些操作时,访问者模式提供了一种清晰的解决方案。
结构
- 访问者接口:定义访问的方法。
- 具体访问者:实现访问者接口,定义具体的操作。
- 元素接口:定义接受访问者的方法。
- 具体元素:实现元素接口,接受访问者。
示例
// 访问者接口
interface Visitor {
void visit(ElementA elementA);
void visit(ElementB elementB);
}
// 具体访问者
class ConcreteVisitor implements Visitor {
@Override
public void visit(ElementA elementA) {
System.out.println("Visiting Element A");
}
@Override
public void visit(ElementB elementB) {
System.out.println("Visiting Element B");
}
}
// 元素接口
interface Element {
void accept(Visitor visitor);
}
class ElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素B
class ElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 使用示例
public class VisitorPatternExample {
public static void main(String[] args) {
ElementA elementA = new ElementA();
ElementB elementB = new ElementB();
Visitor visitor = new ConcreteVisitor();
elementA.accept(visitor); // 访问ElementA
elementB.accept(visitor); // 访问ElementB
}
}
优点
- 将操作与对象结构分离,使得新增操作变得更加容易。
- 可以对对象结构中的元素进行多种操作,而无需修改元素的结构。
缺点
- 增加了新元素时的复杂性,因为需要修改访问者接口和所有的具体访问者。
- 对象结构中的元素与访问者的紧密耦合可能会影响可维护性。
10. 解释器模式(Interpreter Pattern)
定义
解释器模式为一种语言的文法定义一个表示,并定义一个解释器,用于处理该语言的句子。
动机
在需要解释一些特定语言或表达式的场景下,解释器模式可以简化语言处理的复杂度。
结构
- 抽象表达式:定义一个接口以实现解释操作。
- 终结符表达式:实现与文法中的终结符相关的解释操作。
- 非终结符表达式:实现与文法中的非终结符相关的解释操作。
- 上下文:存储解释器所需的状态信息。
示例
import java.util.HashMap;
import java.util.Map;
// 抽象表达式
interface Expression {
int interpret(Map<String, Expression> variables);
}
// 终结符表达式
class TerminalExpression implements Expression {
private String key;
public TerminalExpression(String key) {
this.key = key;
}
@Override
public int interpret(Map<String, Expression> variables) {
Expression variable = variables.get(key);
return variable.interpret(variables);
}
}
// 非终结符表达式
class AddExpression implements Expression {
private Expression leftExpression;
private Expression rightExpression;
public AddExpression(Expression left, Expression right) {
leftExpression = left;
rightExpression = right;
}
@Override
public int interpret(Map<String, Expression> variables) {
return leftExpression.interpret(variables) + rightExpression.interpret(variables);
}
}
// 使用示例
public class InterpreterPatternExample {
public static void main(String[] args) {
// 定义变量
Map<String, Expression> variables = new HashMap<>();
variables.put("x", new TerminalExpression("5"));
variables.put("y", new TerminalExpression("10"));
// 表达式:x + y
Expression expression = new AddExpression(variables.get("x"), variables.get("y"));
int result = expression.interpret(variables);
System.out.println("Result: " + result); // 输出:Result: 15
}
}
优点
- 可以通过组合简单的表达式来构建复杂的表达式。
- 符合开闭原则,新增表达式时可以很容易地扩展。
缺点
- 解析复杂的文法时,可能会导致类的数量急剧增加,影响可读性。
- 性能问题:对于大型文法的解释可能会有性能瓶颈。