设计模式

135 阅读22分钟

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
    }
}
优点
  • 可以通过组合简单的表达式来构建复杂的表达式。
  • 符合开闭原则,新增表达式时可以很容易地扩展。
缺点
  • 解析复杂的文法时,可能会导致类的数量急剧增加,影响可读性。
  • 性能问题:对于大型文法的解释可能会有性能瓶颈。