Java 设计模式(行为型)

发布于:2024-05-05 ⋅ 阅读:(33) ⋅ 点赞:(0)

策略模式

策略模式是一种行为设计模式,它定义了一系列算法,将每个算法封装在独立的策略类中,并使它们可以互相替换。策略模式可以让算法独立于客户端而变化,使得算法可以在不影响客户端的情况下动态地切换或替换。

  • 结构

    1. 策略接口(Strategy):定义了一个公共接口,用于所有具体策略类的统一行为。
    2. 具体策略类(ConcreteStrategy):实现了策略接口,提供了具体的算法实现。
    3. 环境类(Context):持有一个策略对象,并在需要时调用其算法。
  • 场景

    1. 算法替换:当需要在不同情况下使用不同的算法时,可以使用策略模式。例如,不同的排序算法可以根据数据量的大小来选择。
    2. 消除条件语句:当代码中存在大量的条件语句,并且这些条件语句会随着需求的变化而频繁变化时,可以考虑使用策略模式。
    3. 商业规则:在应用中经常会有需要根据不同的商业规则来计算价格或者费用的情况,可以使用策略模式来封装这些规则。
    4. 动态选择算法:当需要根据运行时的条件来选择合适的算法时,策略模式可以派上用场。例如,在游戏中根据不同的难度级别选择不同的敌人AI算法。
  • 优点

    1. 灵活性:策略模式允许在运行时动态切换算法,使得客户端代码不需要了解算法的实现细节。
    2. 可扩展性:由于每个算法都被封装在自己的类中,可以轻松地添加新的算法,而无需修改现有的代码。
    3. 可维护性:每个算法都在单独的类中实现,使得代码更加模块化,易于维护和理解。
    4. 避免条件语句:策略模式可以避免大量的条件语句,使得代码更清晰、更易于理解。
  • 缺点

    1. 增加类的数量:使用策略模式会增加类的数量,可能会使得代码变得更复杂。
    2. 客户端必须了解策略:客户端必须了解不同策略之间的差异,并选择合适的策略,3. 这可能增加了客户端的复杂性。
  • 示例

// 定义支付策略接口
interface PaymentStrategy {
    void pay(double amount);
}

// 实现不同支付策略的具体类
class CreditCardPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用信用卡支付: " + amount + "元");
    }
}

class PayPalPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用PayPal支付: " + amount + "元");
    }
}

class WechatPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用微信支付: " + amount + "元");
    }
}

// 购物车类
class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    // 模拟计算购物车总价
    public double calculateTotal() {
        // 假设这里是实际的计算购物车总价的过程
        return 100.0;
    }

    // 支付方法,根据支付策略进行支付
    public void pay() {
        double totalAmount = calculateTotal();
        paymentStrategy.pay(totalAmount);
    }
}

// 测试类
public class StrategyPatternExample {
    public static void main(String[] args) {
        // 创建购物车对象
        ShoppingCart cart = new ShoppingCart();

        // 选择支付方式
        PaymentStrategy creditCardPayment = new CreditCardPayment();
        PaymentStrategy paypalPayment = new PayPalPayment();
        PaymentStrategy wechatPayment = new WechatPayment();

        // 设置支付方式
        cart.setPaymentStrategy(creditCardPayment);
        // 进行支付
        cart.pay();

        // 设置另一种支付方式
        cart.setPaymentStrategy(paypalPayment);
        // 进行支付
        cart.pay();

        // 设置第三种支付方式
        cart.setPaymentStrategy(wechatPayment);
        // 进行支付
        cart.pay();
    }
}
  • 输出结果
使用信用卡支付: 100.0元
使用PayPal支付: 100.0元
使用微信支付: 100.0

迭代器模式

迭代器模式是一种行为设计模式,用于提供一种方法来顺序访问聚合对象中的元素,而不需要暴露其内部表示。通过使用迭代器模式,可以在不暴露聚合对象的内部结构的情况下,遍历聚合对象中的元素。

  • 结构

    1. 迭代器接口(Iterator):定义了访问和遍历元素的方法,通常包括 hasNext()、next() 等方法。
    2. 具体迭代器(ConcreteIterator):实现了迭代器接口,负责管理遍历聚合对象中元素的状态。
    3. 聚合接口(Aggregate):定义了创建迭代器对象的方法。
    4. 具体聚合类(ConcreteAggregate):实现了聚合接口,创建并返回一个具体的迭代器对象,用于遍历聚合对象中的元素。
  • 场景

    1. 遍历集合:迭代器模式最常见的应用场景是遍历集合,如列表、数组等。
    2. 封装集合:当需要隐藏集合的内部结构,只提供遍历接口时,可以使用迭代器模式。
    3. 支持多种遍历方式:当需要支持多种不同的遍历方式时,可以使用迭代器模式。例如,顺序遍历、逆序遍历等。
    4. 自定义数据结构:当需要自定义数据结构,并希望可以通过迭代器来遍历其中的元素时,可以使用迭代器模式。
  • 优点

    1. 简化客户端代码:客户端不需要了解聚合对象的内部结构,只需要通过迭代器接口来遍历元素。
    2. 解耦性:将迭代器的实现与聚合对象的实现分离,使得两者可以独立变化。
    3. 支持多种遍历方式:可以根据需求创建不同的具体迭代器类来支持不同的遍历方式,如顺序遍历、逆序遍历等。
  • 缺点

    1. 增加了类的数量:引入了迭代器接口和具体迭代器类,可能会增加代码的复杂性。
    2. 遍历速度慢:在某些情况下,使用迭代器模式可能会导致遍历速度比直接访问聚合对象慢,因为迭代器需要维护自身的状态信息。
  • 示例

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

// 迭代器接口
interface Iterator<T> {
    boolean hasNext();
    T next();
}

// 具体迭代器类
class ConcreteIterator<T> implements Iterator<T> {
    private List<T> list;
    private int position;

    public ConcreteIterator(List<T> list) {
        this.list = list;
        this.position = 0;
    }

    @Override
    public boolean hasNext() {
        return position < list.size();
    }

    @Override
    public T next() {
        if (hasNext()) {
            T item = list.get(position);
            position++;
            return item;
        }
        return null;
    }
}

// 聚合接口
interface Aggregate<T> {
    Iterator<T> createIterator();
}

// 具体聚合类
class ConcreteAggregate<T> implements Aggregate<T> {
    private List<T> list;

    public ConcreteAggregate() {
        this.list = new ArrayList<>();
    }

    public void addItem(T item) {
        list.add(item);
    }

    @Override
    public Iterator<T> createIterator() {
        return new ConcreteIterator<>(list);
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建具体聚合类
        ConcreteAggregate<String> aggregate = new ConcreteAggregate<>();

        // 向聚合类中添加元素
        aggregate.addItem("Item 1");
        aggregate.addItem("Item 2");
        aggregate.addItem("Item 3");

        // 创建迭代器并遍历元素
        Iterator<String> iterator = aggregate.createIterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            System.out.println(item);
        }
    }
}
  • 输出结果
Item 1
Item 2
Item 3

访问者模式

访问者模式是一种行为设计模式,用于在不改变元素类的前提下定义作用于元素对象的新操作。它将操作封装在独立的访问者类中,使得可以在不修改元素类的情况下,根据不同的访问者对象,实现对元素对象的不同操作。

  • 结构

    1. 访问者(Visitor)接口:定义了访问者可以访问的元素的操作。
    2. 具体访问者(ConcreteVisitor):实现了访问者接口,并定义了对每个元素的具体操作。
    3. 元素(Element)接口:定义了接受访问者的操作。
    4. 具体元素(ConcreteElement):实现了元素接口,通常包含一个accept方法,用于接受访问者。
    5. 对象结构(Object Structure):包含元素的集合,通常提供一个接受访问者的方法。
  • 场景

    1. 数据结构与操作分离:当数据结构和其操作之间有较多的变化且需要解耦时,访问者模式是一个很好的选择。
    2. 复杂数据结构的遍历:当需要对复杂的数据结构进行多种不同的操作时,访问者模式可以将这些操作封装在不同的访问者中,使得操作逻辑清晰。
    3. 编译器设计:访问者模式常用于编译器的设计中,用于在语法树上执行不同的操作,例如语法分析、类型检查等。
  • 优点

    1. 易扩展:可以通过添加新的访问者来增加新的操作,而无需修改现有代码。
    2. 解耦:将数据结构与算法解耦,使得数据结构可以独立变化而不影响算法。
    3. 单一职责原则:每个具体访问者只负责一个特定的操作,符合单一职责原则。
  • 缺点

    1. 违反封装:访问者模式要求元素公开accept方法,从而暴露了元素的内部细节。
    2. 增加新元素困难:如果需要在对象结构中添加新类型的元素,需要修改所有的访问者接口和具体访问者实现。
  • 示例

import java.util.ArrayList;
import java.util.List;

// 元素接口
interface Element {
    void accept(Visitor visitor);
}

// 具体元素类
class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public void operationA() {
        System.out.println("ConcreteElementA is performing operation A.");
    }
}

// 具体元素类
class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public void operationB() {
        System.out.println("ConcreteElementB is performing operation B.");
    }
}

// 访问者接口
interface Visitor {
    void visit(ConcreteElementA element);

    void visit(ConcreteElementB element);
}

// 具体访问者类
class ConcreteVisitor implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("ConcreteVisitor is visiting ConcreteElementA.");
        element.operationA();
    }

    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("ConcreteVisitor is visiting ConcreteElementB.");
        element.operationB();
    }
}

// 结构对象类
class ObjectStructure {
    private List<Element> elements = new ArrayList<>();

    public void addElement(Element element) {
        elements.add(element);
    }

    public void removeElement(Element element) {
        elements.remove(element);
    }

    public void accept(Visitor visitor) {
        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}

// 客户端类
public class Main {
    public static void main(String[] args) {
        // 创建元素对象
        Element elementA = new ConcreteElementA();
        Element elementB = new ConcreteElementB();

        // 创建访问者对象
        Visitor visitor = new ConcreteVisitor();

        // 创建结构对象
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.addElement(elementA);
        objectStructure.addElement(elementB);

        // 访问元素
        objectStructure.accept(visitor);
    }
}
  • 输出结果
ConcreteVisitor is visiting ConcreteElementA.
ConcreteElementA is performing operation A.
ConcreteVisitor is visiting ConcreteElementB.
ConcreteElementB is performing operation B.

观察者模式

观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生变化时,其所有依赖者(观察者)都会收到通知并自动更新。在观察者模式中,主题(也称为可观察者或被观察者)维护着一组观察者,并在状态发生变化时通知它们。

  • 结构

    1. 主题对象:也称为被观察者或者可观察对象,它维护了一组观察者对象,并提供了添加、删除和通知观察者的方法。主题对象的状态发生变化时,会通知所有注册的观察者。
    2. 观察者对象:观察者对象通过注册到主题对象上,以便在主题状态变化时接收通知。观察者对象通常定义了一个更新方法,用于接收主题对象发出的通知,并作出相应的更新。
    3. 一对多的依赖关系:观察者模式建立了一种一对多的依赖关系,一个主题对象可以有多个观察者对象,而每个观察者对象都只依赖于一个主题对象。
    4. 解耦:观察者模式将主题对象与观察者对象之间解耦,使得它们可以独立变化,而不会相互影响。这样可以降低系统的耦合度,提高代码的灵活性和可维护性。
    5. 动态变化:观察者模式允许动态地添加或删除观察者对象,而不需要修改主题对象的代码,使得系统更加灵活和可扩展。
  • 优点

    1. 解耦:观察者模式将主题对象与观察者对象之间解耦,使得它们可以独立变化,而不会相互影响。
    2. 扩展性:可以随时添加或删除观察者对象,而不影响主题对象或其他观察者对象,使得系统更加灵活和可扩展。
    3. 通知机制:观察者模式提供了一种灵活的通知机制,可以实时地通知观察者对象主题状态的变化,从而实现更紧密的协作。
  • 缺点

    1. 过多通知:如果主题对象的状态变化频繁,可能会导致观察者对象频繁地收到通知,造成性能上的开销。
    2. 循环依赖:如果观察者对象之间相互依赖,可能会导致循环依赖的问题,需要谨慎设计。
  • 场景

    1. 事件处理:GUI编程中常用观察者模式处理用户界面组件的事件。
    2. 消息通知:发布/订阅模式是观察者模式的一种变体,常用于实现消息队列等系统。
    3. 状态监控:当需要监控对象的状态变化并作出相应响应时,观察者模式是一个常见的选择。
    4. 发布订阅系统:用于构建解耦的事件驱动架构,其中发布者和订阅者之间没有直接的依赖关系。
  • 示例

import java.util.ArrayList;
import java.util.List;

// 主题接口(Subject Interface)
interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers();
    int getState();
}

// 具体主题类(Concrete Subject Class)
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        notifyObservers();
    }

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

// 观察者接口(Observer Interface)
interface Observer {
    void update();
}

// 具体观察者类(Concrete Observer Class)
class ConcreteObserver implements Observer {
    private Subject subject;

    public ConcreteObserver(Subject subject) {
        this.subject = subject;
    }

    public void update() {
        System.out.println("Observer received update. New state: " + subject.getState());
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        // 创建具体主题对象
        ConcreteSubject subject = new ConcreteSubject();

        // 创建具体观察者对象,并将主题对象注册为观察者
        ConcreteObserver observer = new ConcreteObserver(subject);
        subject.attach(observer);

        // 改变主题对象的状态
        subject.setState(10);

        // 取消观察者的注册
        subject.detach(observer);

        // 再次改变主题对象的状态
        subject.setState(20);
    }
}
  • 输出结果
Observer received update. New state: 10

命令模式

命令模式是一种行为设计模式,它将请求封装成一个对象,从而允许用不同的请求对客户进行参数化,并且可以支持请求的排队、日志记录、撤销和重做等操作。

  • 结构

    1. 命令接口(Command):定义了执行请求的方法 execute(),所有具体命令类都要实现该接口。
    2. 具体命令类(ConcreteCommand):实现了命令接口,负责调用接收者执行具体的操作。
    3. 接收者类(Receiver):负责执行命令指定的操作,实际上是命令的真正执行者。
    4. 调用者/请求者类(Invoker):负责调用命令对象来执行请求。它并不知道命令对象如何实现,只是调用命令对象的 execute() 方法来触发请求。
  • 场景

    1. GUI 操作:如菜单项、按钮等用户界面元素,可以使用命令模式来处理用户的操作。
    2. 数据库事务管理:可以将数据库操作封装成命令对象,支持事务的撤销和恢复。
    3. 日志系统:可以使用命令模式记录用户操作,支持撤销和恢复功能。
    4. 任务调度:可以将任务封装成命令对象,支持异步执行、撤销等功能。
    5. 遥控器控制器:如电视遥控器、车辆遥控器等,可以使用命令模式实现不同按钮的操作。
  • 优点

    1. 解耦合:命令模式将请求发送者和接收者解耦,发送者不需要知道接收者的具体实现,只需知道命令对象即可。
    2. 可扩展性:可以轻易地添加新的命令,而不需要修改现有的代码结构。
    3. 支持撤销和恢复操作:通过存储命令的历史记录,可以轻松实现撤销和恢复功能。
    4. 支持组合命令:可以将多个命令组合成一个复合命令,从而实现更复杂的操作。
  • 缺点

    1. 代码量增加:引入了额外的命令对象,可能会导致代码量增加。
    2. 可能导致过多的具体命令类:如果系统中有大量的具体命令类,可能会增加系统的复杂度。
  • 示例

import java.util.*;

// 命令接口
interface Command {
    void execute();
}

// 具体命令:开灯
class TurnOnLightCommand implements Command {
    private Light light;

    public TurnOnLightCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOn();
    }
}

// 具体命令:关灯
class TurnOffLightCommand implements Command {
    private Light light;

    public TurnOffLightCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOff();
    }
}

// 接收者:灯
class Light {
    public void turnOn() {
        System.out.println("灯已打开");
    }

    public void turnOff() {
        System.out.println("灯已关闭");
    }
}

// 调用者:遥控器
class RemoteControl {
    private Map<String, Command> commands = new HashMap<>();

    public void setCommand(String commandName, Command command) {
        commands.put(commandName, command);
    }

    public void pressButton(String commandName) {
        Command command = commands.get(commandName);
        if (command != null) {
            command.execute();
        } else {
            System.out.println("未找到命令:" + commandName);
        }
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        // 创建灯对象
        Light light = new Light();

        // 创建命令对象
        Command turnOnCommand = new TurnOnLightCommand(light);
        Command turnOffCommand = new TurnOffLightCommand(light);

        // 创建遥控器对象
        RemoteControl remoteControl = new RemoteControl();

        // 设置遥控器按钮对应的命令
        remoteControl.setCommand("开灯", turnOnCommand);
        remoteControl.setCommand("关灯", turnOffCommand);

        // 模拟按下遥控器按钮
        remoteControl.pressButton("开灯"); // 输出:灯已打开
        remoteControl.pressButton("关灯"); // 输出:灯已关闭
    }
}
  • 输出结果
灯已打开
灯已关闭

模板方法模式

模板方法模式是一种行为设计模式,用于定义一个算法的框架,将一些步骤延迟到子类中实现。模板方法模式定义了一个抽象类,其中包含了算法的骨架和一些基本方法,而具体的实现则由子类来完成。

  • 结构

    1. 定义模板方法:在父类中定义一个模板方法,该方法包含算法的骨架,其中包含了算法的执行步骤和调用顺序。模板方法可以是抽象的或者具体的,但其中至少包含一个或多个抽象方法用于表示算法的某些步骤。
    2. 抽象方法:在父类中定义抽象方法,用于表示算法的不同步骤。这些方法在模板方法中被调用,但其具体实现由子类来提供。
    3. 具体方法:除了抽象方法外,模板方法模式中的父类可能还包含一些具体的方法,这些方法是所有子类共享的,它们不应该被子类覆盖。
    4. 子类实现:子类继承父类,并实现父类定义的抽象方法,以完成具体的步骤实现。子类可以选择性地覆盖父类的具体方法,以满足特定的需求。
    5. 模板方法调用:客户端通过调用父类的模板方法来执行算法。在执行过程中,父类的模板方法负责调用各个步骤的抽象方法,而具体的步骤实现则由子类提供。
  • 场景

    1. 框架设计:模板方法模式常用于框架设计中,框架定义算法的骨架,而具体的业务逻辑由使用框架的开发者来实现。
    2. 代码复用:当存在多个类似的算法,但其中一些步骤可能有所不同时,可以使用模板方法模式,将公共部分提取到父类中。
    3. 算法的执行顺序固定:当需要确保算法的执行顺序和步骤固定不变时,可以使用模板方法模式。
    4. 规范行为:当需要在多个子类中保持一致的行为时,可以使用模板方法模式来确保统一实现。
  • 优点

    1. 代码复用:模板方法模式将算法的公共部分封装在父类中,可以在多个子类中复用。
    2. 灵活性:模板方法模式允许子类为特定的步骤提供自定义的实现,从而使得整个算法的行为可以在子类中灵活变化。
    3. 统一流程:模板方法模式确保了算法的执行顺序和步骤,避免了在子类中不同实现中可能导致的错误。
  • 缺点

    1. 限制子类方法:模板方法模式可能会限制子类方法的灵活性,因为子类必须遵循父类定义的模板方法结构。
    2. 复杂性:当算法的步骤较多时,模板方法模式可能会导致父类的方法变得复杂,难以维护和理解。
  • 示例

// 咖啡制作的抽象类
abstract class CoffeeTemplate {

    // 冲泡咖啡的模板方法
    public final void makeCoffee() {
        boilWater(); // 烧水
        brewCoffee(); // 冲泡咖啡
        pourInCup(); // 倒入杯中
        addCondiments(); // 加入调料
    }

    // 抽象方法:冲泡咖啡
    abstract void brewCoffee();

    // 抽象方法:加入调料
    abstract void addCondiments();

    // 具体方法:烧水
    void boilWater() {
        System.out.println("烧开水");
    }

    // 具体方法:倒入杯中
    void pourInCup() {
        System.out.println("将咖啡倒入杯中");
    }
}

// 具体咖啡类:咖啡因咖啡
class CaffeineCoffee extends CoffeeTemplate {

    @Override
    void brewCoffee() {
        System.out.println("用沸水冲泡咖啡因咖啡");
    }

    @Override
    void addCondiments() {
        System.out.println("加入牛奶和糖");
    }
}

// 具体咖啡类:无咖啡因咖啡
class DecafCoffee extends CoffeeTemplate {

    @Override
    void brewCoffee() {
        System.out.println("用沸水冲泡无咖啡因咖啡");
    }

    @Override
    void addCondiments() {
        System.out.println("加入柠檬");
    }
}

// 测试类
public class TemplatePatternExample {
    public static void main(String[] args) {
        // 冲泡咖啡因咖啡
        CoffeeTemplate caffeineCoffee = new CaffeineCoffee();
        System.out.println("制作咖啡因咖啡:");
        caffeineCoffee.makeCoffee();

        System.out.println();

        // 冲泡无咖啡因咖啡
        CoffeeTemplate decafCoffee = new DecafCoffee();
        System.out.println("制作无咖啡因咖啡:");
        decafCoffee.makeCoffee();
    }
}
  • 输出结果
制作咖啡因咖啡:
烧开水
用沸水冲泡咖啡因咖啡
将咖啡倒入杯中
加入牛奶和糖

制作无咖啡因咖啡:
烧开水
用沸水冲泡无咖啡因咖啡
将咖啡倒入杯中
加入柠檬

事件驱动模式

事件驱动模式是一种设计模式,它基于事件的触发和响应机制来组织和管理程序的逻辑。在事件驱动模式中,系统中的各个组件通过订阅和发布事件的方式来进行通信和协作,当特定的事件发生时,订阅了该事件的组件会接收到通知并执行相应的处理逻辑。

  • 结构

    1. 事件(Event):系统中发生的某种特定的事情或状态变化,可以是用户操作、系统消息、时间触发等。
    2. 事件源(Event Source):产生事件的对象或组件,也称为发布者(Publisher)。
    3. 事件监听器(Event Listener):订阅特定类型事件的对象或组件,也称为订阅者(Subscriber),用于接收事件并执行相应的处理逻辑。
    4. 事件处理器(Event Handler):用于处理特定类型事件的逻辑代码,通常由事件监听器调用。
  • 场景

    1. GUI 应用程序:事件驱动模式广泛应用于图形用户界面(GUI)应用程序中,例如按钮点击事件、鼠标移动事件等。
    2. 网络编程:事件驱动模式也常用于网络编程中,例如处理客户端连接、数据到达等事件。
    3. 用户交互:用户与系统的交互往往是事件驱动的,例如用户点击按钮、输入文本等。
    4. 消息队列:事件驱动模式也可以用于实现消息队列系统,通过发布和订阅事件来实现消息的传递和处理。
  • 优点

    1. 解耦性:事件驱动模式将事件源和事件处理器解耦,各个组件之间的通信通过事件来进行,降低了组件之间的依赖性。
    2. 灵活性:事件驱动模式使得系统更加灵活,可以根据需求动态添加或移除事件监听器,而不影响其他组件的正常运行。
    3. 可维护性:事件驱动模式将系统的逻辑分解为多个小的事件和处理器,使得系统更易于理解和维护。
  • 缺点

    1. 调试困难:事件驱动模式使得程序的执行流程变得不明显,调试时可能需要跟踪多个事件处理器之间的交互。
    2. 性能开销:事件驱动模式可能会引入一定的性能开销,因为需要在事件源产生事件时通知所有订阅了该事件的监听器。
  • 示例

import java.util.ArrayList;
import java.util.List;

// 定义事件类
class MyEvent {
    private String message;

    public MyEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

// 定义事件监听器接口
interface MyEventListener {
    void onEvent(MyEvent event);
}

// 定义事件源类
class MyEventSource {
    private List<MyEventListener> listeners = new ArrayList<>();

    // 注册事件监听器
    public void addListener(MyEventListener listener) {
        listeners.add(listener);
    }

    // 触发事件
    public void fireEvent(MyEvent event) {
        for (MyEventListener listener : listeners) {
            listener.onEvent(event);
        }
    }
}

// 示例:事件监听器的实现
class MyListener implements MyEventListener {
    @Override
    public void onEvent(MyEvent event) {
        System.out.println("Received event: " + event.getMessage());
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建事件源对象
        MyEventSource eventSource = new MyEventSource();

        // 创建事件监听器对象
        MyEventListener listener = new MyListener();

        // 注册事件监听器
        eventSource.addListener(listener);

        // 触发事件
        eventSource.fireEvent(new MyEvent("Hello, world!"));
    }
}
  • 输出结果
Received event: Hello, world!

责任链模式

责任链模式是一种行为设计模式,它允许将请求沿着处理链传递,直到有一个处理者能够处理它为止。在责任链模式中,每个处理者都包含对下一个处理者的引用,当一个请求到达时,处理者可以选择处理请求或者将请求传递给下一个处理者。

  • 结构

    1. Handler(处理器):定义了处理请求的接口,并维护了一个指向下一个处理器的引用。
    2. ConcreteHandler(具体处理器):实现了处理器接口,负责处理特定类型的请求,或者将请求传递给链中的下一个处理器。
    3. Client(客户端):创建和提交请求给责任链的起始处理器。
  • 场景

    1. 请求处理管道:例如,HTTP请求处理,每个处理器负责处理特定类型的请求(如路由、身份验证、日志记录等)。
    2. 审批流程:例如,工作流程中的审批流程,每个处理器负责一个级别的审批,或者根据请求的属性决定是否进行审批。
    3. 异常处理:例如,在Java中的异常处理中,可以通过责任链模式来处理不同类型的异常。
    4. 日志记录:例如,记录日志时可以使用责任链模式,每个处理器负责不同级别或不同类型的日志记录。
  • 优点

    1. 解耦:将请求的发送者和接收者解耦,发送者不需要知道请求是如何处理以及谁处理了它。
    2. 灵活性:可以动态地调整或扩展责任链,无需修改现有代码。
    3. 可维护性:责任链模式使得代码易于维护和理解,每个处理器只负责一种特定的责任。
    4. 可配置性:可以通过配置链中的处理器来改变处理顺序或新增处理器,而无需修改代码。
  • 缺点

    1. 性能:请求可能会在责任链的多个处理器之间传递,如果责任链过长或处理器处理请求的时间过长,可能会影响性能。
    2. 请求保证:不能保证请求一定会被处理,如果没有合适的处理器处理请求,请求可能会到达链的末尾而未被处理。
  • 示例

// 请求类
class Request {
    private String content;
    
    public Request(String content) {
        this.content = content;
    }
    
    public String getContent() {
        return content;
    }
}

// 抽象处理器类
abstract class Handler {
    protected Handler nextHandler;
    
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }
    
    public abstract void handleRequest(Request request);
}

// 具体处理器类
class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getContent().equals("A")) {
            System.out.println("ConcreteHandlerA handles the request: " + request.getContent());
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        } else {
            System.out.println("No handler can process the request.");
        }
    }
}

// 具体处理器类
class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getContent().equals("B")) {
            System.out.println("ConcreteHandlerB handles the request: " + request.getContent());
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        } else {
            System.out.println("No handler can process the request.");
        }
    }
}

// 具体处理器类
class ConcreteHandlerC extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getContent().equals("C")) {
            System.out.println("ConcreteHandlerC handles the request: " + request.getContent());
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        } else {
            System.out.println("No handler can process the request.");
        }
    }
}

// 客户端类
public class Main {
    public static void main(String[] args) {
        // 创建处理器对象
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();
        Handler handlerC = new ConcreteHandlerC();
        
        // 设置处理器的顺序
        handlerA.setNextHandler(handlerB);
        handlerB.setNextHandler(handlerC);
        
        // 创建请求对象
        Request request1 = new Request("A");
        Request request2 = new Request("B");
        Request request3 = new Request("C");
        Request request4 = new Request("D");
        
        // 处理请求
        handlerA.handleRequest(request1);
        handlerA.handleRequest(request2);
        handlerA.handleRequest(request3);
        handlerA.handleRequest(request4);
    }
}
  • 输出结果
ConcreteHandlerA handles the request: A
ConcreteHandlerB handles the request: B
ConcreteHandlerC handles the request: C
No handler can process the request.

中介者模式

中介者模式是一种行为设计模式,它允许对象之间通过一个中介者对象进行交互,而不直接相互引用。中介者模式促进了松耦合,因为它将对象之间的通信和协作集中在一个对象中,而不是分散在各个对象之间。

  • 结构

    1. 中介者(Mediator):定义了一个接口,用于与各个同事对象通信。
    2. 具体中介者(ConcreteMediator):实现了中介者接口,并负责协调各个同事对象的交互。
    3. 同事(Colleague):定义了一个接口,用于与中介者通信。
    4. 具体同事(ConcreteColleague):实现了同事接口,并与其他同事对象进行通信,当需要与其他同事对象通信时,会通过中介者来进行。
  • 场景

    1. 多个对象之间需要进行复杂的通信:当多个对象之间需要进行复杂的通信和协作时,可以使用中介者模式来简化对象之间的关系。
    2. 系统中的对象之间的耦合度较高:当系统中的对象之间的耦合度较高,导致修改一个对象会影响其他对象时,可以使用中介者模式来减少对象之间的耦合度。
    3. 分布式系统中的通信:在分布式系统中,不同节点之间需要进行通信和协作,可以使用中介者模式来简化节点之间的通信逻辑。
  • 优点

    1. 减少了对象之间的耦合:中介者模式将对象之间的通信集中在一个中介者对象中,使得对象之间的关系变得简单且松耦合。
    2. 简化了对象的交互:中介者模式将对象之间的交互逻辑封装在中介者对象中,使得对象之间的通信更加简单清晰。
    3. 易于扩展:由于中介者模式将对象之间的交互封装在一个中介者对象中,因此很容易添加新的同事对象或调整其交互逻辑。
  • 缺点

    1. 中介者对象可能变得庞大复杂:随着系统的复杂度增加,中介者对象可能会变得庞大复杂,因此需要谨慎设计中介者对象的职责。
    2. 可能会导致性能问题:中介者模式可能会导致中介者对象成为系统的瓶颈,因为所有的交互都要通过中介者对象进行,可能会影响性能。
  • 示例

// 中介者接口
interface Mediator {
    void sendMessage(String message, Colleague colleague);
}

// 具体中介者类
class ConcreteMediator implements Mediator {
    private Colleague colleague1;
    private Colleague colleague2;

    public void setColleague1(Colleague colleague1) {
        this.colleague1 = colleague1;
    }

    public void setColleague2(Colleague colleague2) {
        this.colleague2 = colleague2;
    }

    @Override
    public void sendMessage(String message, Colleague colleague) {
        if (colleague == colleague1) {
            colleague2.receiveMessage(message);
        } else if (colleague == colleague2) {
            colleague1.receiveMessage(message);
        }
    }
}

// 抽象同事类
abstract class Colleague {
    protected Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }

    public abstract void send(String message);

    public abstract void receiveMessage(String message);
}

// 具体同事类
class ConcreteColleague1 extends Colleague {
    public ConcreteColleague1(Mediator mediator) {
        super(mediator);
    }

    @Override
    public void send(String message) {
        System.out.println("ConcreteColleague1 sends message: " + message);
        mediator.sendMessage(message, this);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println("ConcreteColleague1 receives message: " + message);
    }
}

// 具体同事类
class ConcreteColleague2 extends Colleague {
    public ConcreteColleague2(Mediator mediator) {
        super(mediator);
    }

    @Override
    public void send(String message) {
        System.out.println("ConcreteColleague2 sends message: " + message);
        mediator.sendMessage(message, this);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println("ConcreteColleague2 receives message: " + message);
    }
}

// 客户端类
public class Main {
    public static void main(String[] args) {
        // 创建中介者对象
        ConcreteMediator mediator = new ConcreteMediator();

        // 创建具体同事对象
        ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
        ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);

        // 设置同事对象到中介者中
        mediator.setColleague1(colleague1);
        mediator.setColleague2(colleague2);

        // 同事之间进行通信
        colleague1.send("Hello, colleague2!");
        colleague2.send("Hi, colleague1!");
    }
}
  • 输出结果
ConcreteColleague1 sends message: Hello, colleague2!
ConcreteColleague2 receives message: Hello, colleague2!
ConcreteColleague2 sends message: Hi, colleague1!
ConcreteColleague1 receives message: Hi, colleague1!

状态模式

状态模式是一种行为设计模式,用于允许对象在其内部状态改变时改变其行为。它将对象的行为封装在不同的状态类中,并使得对象在不同状态下有不同的行为。这使得状态的切换不会影响到对象的行为,从而使得状态的改变更加灵活和可扩展。

  • 结构

    1. Context(上下文):定义客户端感兴趣的接口,并且维护一个具体状态子类的实例,这个实例定义当前的状态。
    2. State(状态):定义一个接口,用于封装与Context的一个特定状态相关的行为。
    3. ConcreteState(具体状态):每个具体状态类实现State接口,并且封装了与该状态相关的行为。
  • 场景

    1. 当一个对象的行为取决于它的状态,并且在运行时可以动态地改变其状态时,可以考虑使用状态模式。例如,交通信号灯的颜色取决于当前的状态(红灯、绿灯、黄灯)。
    2. 当一个对象需要根据不同的状态执行不同的行为时,可以考虑使用状态模式。例如,一个文档编辑器根据当前的编辑模式(插入模式、替换模式)执行不同的编辑操作。
    3. 当系统中存在大量相似的条件语句来控制对象的行为时,可以考虑使用状态模式来代替这些条件语句,使得系统更加灵活和可维护。
  • 优点

    1. 封装性:状态模式将状态的转移逻辑分布到具体状态类中,使得每个状态都具有自己的行为,从而更好地封装了对象的状态。
    2. 灵活性:状态模式使得新增加状态变得更加容易,同时也可以根据需要动态地改变对象的状态,而不需要修改原有的代码。
    3. 可维护性:由于状态的转移逻辑被分布到各个具体状态类中,因此可以更容易地理解和维护代码。
  • 缺点

    1. 类膨胀:状态模式可能会导致具体状态类的数量增加,从而增加了系统的复杂性。
    2. 逻辑分散:状态模式将状态的逻辑分散到各个具体状态类中,可能会导致系统的逻辑分散和不易理解。
  • 示例

// 状态接口
interface TrafficLightState {
    void handle(TrafficLight light);
}

// 具体状态类:红灯状态
class RedLightState implements TrafficLightState {
    @Override
    public void handle(TrafficLight light) {
        System.out.println("Red Light: Stop!");
        light.setState(new GreenLightState());
    }
}

// 具体状态类:绿灯状态
class GreenLightState implements TrafficLightState {
    @Override
    public void handle(TrafficLight light) {
        System.out.println("Green Light: Go!");
        light.setState(new YellowLightState());
    }
}

// 具体状态类:黄灯状态
class YellowLightState implements TrafficLightState {
    @Override
    public void handle(TrafficLight light) {
        System.out.println("Yellow Light: Prepare to stop!");
        light.setState(new RedLightState());
    }
}

// 上下文类
class TrafficLight {
    private TrafficLightState state;

    public TrafficLight() {
        // 初始状态为红灯
        this.state = new RedLightState();
    }

    public void setState(TrafficLightState state) {
        this.state = state;
    }

    public void change() {
        state.handle(this);
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        TrafficLight trafficLight = new TrafficLight();

        // 初始状态:红灯
        trafficLight.change();

        // 状态切换:红灯 -> 绿灯
        trafficLight.change();

        // 状态切换:绿灯 -> 黄灯
        trafficLight.change();

        // 状态切换:黄灯 -> 红灯
        trafficLight.change();
    }
}
  • 输出结果
Red Light: Stop!
Green Light: Go!
Yellow Light: Prepare to stop!
Red Light: Stop!