【Java实战㉓】Java设计模式实战:解锁行为型模式的强大力量

发布于:2025-09-05 ⋅ 阅读:(17) ⋅ 点赞:(0)


一、观察者模式实战

1.1 观察者模式概念

观察者模式(Observer Pattern),也被称为发布 - 订阅(Publish/Subscribe)模式,是一种行为型设计模式。它定义了对象之间的一对多依赖关系,当一个对象(被称为主题 Subject)状态发生改变时,所有依赖于它的对象(被称为观察者 Observer)都会得到通知并被自动更新。

在观察者模式中,主题就像是一个消息发布者,它不关心具体有哪些观察者在关注它,只负责在自身状态变化时通知所有已注册的观察者。而观察者则是消息的接收者,它们关注主题的状态变化,一旦收到通知,就会执行相应的操作。这种模式在许多场景中都有广泛应用,比如社交媒体动态提醒、股票价格变化通知等。例如在微博上,当你关注的博主发布了新动态,你作为观察者就会收到推送通知,然后可以去点赞、评论或者转发,博主就是主题,粉丝们就是观察者。

1.2 观察者模式实现

在 Java 中,我们可以使用java.util.Observable类和java.util.Observer接口来实现观察者模式。

  • Observer接口:它定义了一个update方法,当观察者收到主题的通知时,会调用这个方法来更新自己的状态。每个具体的观察者类都需要实现这个接口。例如:
import java.util.Observable;
import java.util.Observer;

public class ConcreteObserver implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        // 在这里实现收到通知后的具体操作
        System.out.println("收到通知,执行相应操作,更新状态:" + arg);
    }
}
  • Observable类:它是一个被观察的对象,也就是主题。它提供了一些方法来管理观察者,比如添加观察者(addObserver)、删除观察者(deleteObserver)以及通知所有观察者(notifyObservers)等。具体使用时,我们需要继承这个类并实现相关逻辑。例如:
import java.util.Observable;

public class ConcreteSubject extends Observable {
    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        setChanged();// 标记状态已改变
        notifyObservers(state);// 通知所有观察者
    }
}

在上述代码中,ConcreteSubject类继承自Observable,当调用setState方法改变state时,先调用setChanged方法标记状态已改变,然后调用notifyObservers方法通知所有注册的观察者,并且将新的状态state作为参数传递给观察者的update方法。ConcreteObserver类实现了Observer接口,在update方法中定义了收到通知后的具体处理逻辑。

1.3 观察者模式实战案例

以消息订阅与推送系统为例,来展示观察者模式的实际应用。假设我们有一个消息发布中心,有多个用户订阅了不同类型的消息,当有新消息发布时,需要及时推送给对应的用户。

首先定义Observer接口和具体的观察者类:

import java.util.Observable;
import java.util.Observer;

// 观察者接口
public interface MessageObserver extends Observer {
    void update(Observable o, String message);
}

// 具体观察者类:用户A
public class UserA implements MessageObserver {
    @Override
    public void update(Observable o, String message) {
        System.out.println("用户A收到消息:" + message);
    }
}

// 具体观察者类:用户B
public class UserB implements MessageObserver {
    @Override
    public void update(Observable o, String message) {
        System.out.println("用户B收到消息:" + message);
    }
}

然后定义Observable类的子类,即消息发布中心:

import java.util.Observable;

public class MessagePublisher extends Observable {
    private String latestMessage;

    public void setLatestMessage(String message) {
        this.latestMessage = message;
        setChanged();
        notifyObservers(latestMessage);
    }
}

最后是测试代码:

public class MessageSystemTest {
    public static void main(String[] args) {
        MessagePublisher publisher = new MessagePublisher();

        UserA userA = new UserA();
        UserB userB = new UserB();

        publisher.addObserver(userA);
        publisher.addObserver(userB);

        publisher.setLatestMessage("新的重要通知!");

        publisher.deleteObserver(userA);
        publisher.setLatestMessage("另一条消息,用户A不会收到");
    }
}

在这个案例中,MessagePublisher是消息发布中心,也就是主题,UserA和UserB是订阅消息的用户,也就是观察者。当MessagePublisher发布新消息时,会通知所有已注册的用户(观察者),用户收到消息后会进行相应的处理。并且可以动态地添加或删除观察者,比如在测试代码中先添加了UserA和UserB,后来又删除了UserA。

二、策略模式与模板方法模式

2.1 策略模式

策略模式(Strategy Pattern)是一种行为型设计模式 ,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。在策略模式中,有三个主要角色:

  • 策略接口(Strategy):定义了所有具体策略类需要实现的方法,它是具体策略类的抽象,封装了算法的行为。例如,在一个电商系统中计算商品折扣的场景,策略接口可以定义为:
public interface DiscountStrategy {
    double calculateDiscount(double originalPrice);
}
  • 具体策略类(ConcreteStrategy):实现了策略接口,封装了具体的算法实现。比如针对不同的促销活动,有不同的折扣计算策略:
// 无折扣策略
public class NoDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalPrice) {
        return originalPrice;
    }
}

// 满减策略
public class FullReductionDiscountStrategy implements DiscountStrategy {
    private double fullAmount;
    private double reductionAmount;

    public FullReductionDiscountStrategy(double fullAmount, double reductionAmount) {
        this.fullAmount = fullAmount;
        this.reductionAmount = reductionAmount;
    }

    @Override
    public double calculateDiscount(double originalPrice) {
        if (originalPrice >= fullAmount) {
            return originalPrice - reductionAmount;
        }
        return originalPrice;
    }
}

// 百分比折扣策略
public class PercentageDiscountStrategy implements DiscountStrategy {
    private double discountRate;

    public PercentageDiscountStrategy(double discountRate) {
        this.discountRate = discountRate;
    }

    @Override
    public double calculateDiscount(double originalPrice) {
        return originalPrice * (1 - discountRate);
    }
}
  • 上下文类(Context):持有一个策略接口的引用,它负责在运行时选择和使用具体的策略对象。上下文类可以通过构造函数或者方法来设置具体的策略,这样客户端只需要与上下文类交互,而不需要关心具体策略的实现细节。例如:
public class ShoppingCart {
    private DiscountStrategy discountStrategy;

    public ShoppingCart(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public void setDiscountStrategy(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public double checkout(double originalPrice) {
        return discountStrategy.calculateDiscount(originalPrice);
    }
}

通过策略模式,客户端可以在运行时根据不同的条件动态地切换算法。比如在电商系统中,根据不同的促销活动选择不同的折扣计算策略,而不需要修改大量的业务代码,提高了代码的灵活性和可维护性。

2.2 策略模式实战案例

以支付方式选择功能为例,展示策略模式的实际应用。在一个在线购物系统中,用户可以选择多种支付方式进行付款,如信用卡支付、支付宝支付、微信支付等。每种支付方式的支付逻辑不同,使用策略模式可以将这些不同的支付逻辑封装成独立的策略类,方便管理和扩展。

首先定义支付策略接口:

public interface PaymentStrategy {
    void pay(double amount);
}

然后实现具体的支付策略类:

// 信用卡支付策略
public class CreditCardPaymentStrategy implements PaymentStrategy {
    private String cardNumber;

    public CreditCardPaymentStrategy(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public void pay(double amount) {
        System.out.println("使用信用卡支付:" + amount + " 金额,卡号为:" + cardNumber);
    }
}

// 支付宝支付策略
public class AlipayPaymentStrategy implements PaymentStrategy {
    private String account;

    public AlipayPaymentStrategy(String account) {
        this.account = account;
    }

    @Override
    public void pay(double amount) {
        System.out.println("使用支付宝支付:" + amount + " 金额,账号为:" + account);
    }
}

// 微信支付策略
public class WeChatPaymentStrategy implements PaymentStrategy {
    private String openId;

    public WeChatPaymentStrategy(String openId) {
        this.openId = openId;
    }

    @Override
    public void pay(double amount) {
        System.out.println("使用微信支付:" + amount + " 金额,openId为:" + openId);
    }
}

接着定义上下文类,即购物车类:

public class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    public ShoppingCart(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

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

    public void checkout(double amount) {
        paymentStrategy.pay(amount);
    }
}

最后是测试代码:

public class PaymentTest {
    public static void main(String[] args) {
        // 使用信用卡支付
        ShoppingCart cart1 = new ShoppingCart(new CreditCardPaymentStrategy("1234-5678-9101-1121"));
        cart1.checkout(100.0);

        // 切换为支付宝支付
        ShoppingCart cart2 = new ShoppingCart(new AlipayPaymentStrategy("user@alipay.com"));
        cart2.checkout(200.0);

        // 切换为微信支付
        ShoppingCart cart3 = new ShoppingCart(new WeChatPaymentStrategy("openId12345"));
        cart3.checkout(150.0);
    }
}

在这个案例中,PaymentStrategy是支付策略接口,CreditCardPaymentStrategy、AlipayPaymentStrategy和WeChatPaymentStrategy是具体的支付策略类,分别实现了信用卡、支付宝和微信的支付逻辑。ShoppingCart是上下文类,它持有一个PaymentStrategy的引用,通过setPaymentStrategy方法可以动态地切换支付策略。客户端在使用时,只需要创建ShoppingCart对象并传入相应的支付策略,就可以完成支付操作,代码结构清晰,易于扩展新的支付方式。

2.3 模板方法模式

模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。在模板方法模式中,有两个主要角色:

  • 抽象类(AbstractClass):定义了算法的骨架,包含一个或多个模板方法,这些模板方法定义了算法的各个步骤。模板方法通常是用final修饰,防止子类重写整个算法流程。抽象类中还可以包含抽象方法和具体方法,抽象方法需要子类去实现,具体方法可以被子类重写(可选)。例如,在一个数据处理的场景中,定义一个抽象的数据处理类:
public abstract class DataProcessor {
    // 模板方法,定义数据处理的流程
    public final void processData() {
        readData();
        preprocessData();
        analyzeData();
        saveResult();
    }

    // 抽象方法,由子类实现读取数据的逻辑
    protected abstract void readData();

    // 抽象方法,由子类实现数据预处理的逻辑
    protected abstract void preprocessData();

    // 抽象方法,由子类实现数据分析的逻辑
    protected abstract void analyzeData();

    // 具体方法,有默认实现,子类可重写
    protected void saveResult() {
        System.out.println("将处理结果保存到默认位置");
    }
}
  • 具体子类(ConcreteClass):继承抽象类,并实现抽象类中定义的抽象方法。每个具体子类根据自身的业务需求,实现抽象方法的具体逻辑。例如:
public class ExcelDataProcessor extends DataProcessor {
    @Override
    protected void readData() {
        System.out.println("从Excel文件中读取数据");
    }

    @Override
    protected void preprocessData() {
        System.out.println("对Excel数据进行预处理,如去重、格式转换等");
    }

    @Override
    protected void analyzeData() {
        System.out.println("对Excel数据进行分析,计算统计指标等");
    }

    @Override
    protected void saveResult() {
        System.out.println("将Excel数据处理结果保存到指定文件夹");
    }
}

public class JsonDataProcessor extends DataProcessor {
    @Override
    protected void readData() {
        System.out.println("从Json文件中读取数据");
    }

    @Override
    protected void preprocessData() {
        System.out.println("对Json数据进行预处理,解析Json结构等");
    }

    @Override
    protected void analyzeData() {
        System.out.println("对Json数据进行分析,提取关键信息等");
    }
}

通过模板方法模式,将通用的算法流程定义在抽象类中,不同的具体实现细节由子类来完成。这样可以提高代码的复用性,减少重复代码,同时也方便对算法进行扩展和维护。当需要新增一种数据处理类型时,只需要创建一个新的具体子类,实现相应的抽象方法即可,而不需要修改已有的算法骨架代码。

2.4 模板方法模式实战案例

以数据导出模板为例,假设我们需要将不同类型的数据导出为不同格式的文件,如将用户数据导出为 CSV 文件,将订单数据导出为 Excel 文件。虽然导出的具体数据和文件格式不同,但导出的基本流程是相似的,都包括获取数据、格式化数据、创建文件和写入文件等步骤,这种情况非常适合使用模板方法模式。

首先定义抽象的数据导出类:

public abstract class DataExporter {
    // 模板方法,定义数据导出的流程
    public final void exportData() {
        Object data = getData();
        String formattedData = formatData(data);
        createFile();
        writeData(formattedData);
    }

    // 抽象方法,由子类实现获取数据的逻辑
    protected abstract Object getData();

    // 抽象方法,由子类实现格式化数据的逻辑
    protected abstract String formatData(Object data);

    // 抽象方法,由子类实现创建文件的逻辑
    protected abstract void createFile();

    // 抽象方法,由子类实现写入数据的逻辑
    protected abstract void writeData(String formattedData);
}

然后实现具体的数据导出子类,比如用户数据导出为 CSV 文件:

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

public class UserCsvExporter extends DataExporter {
    private List<User> users;

    public UserCsvExporter(List<User> users) {
        this.users = users;
    }

    @Override
    protected Object getData() {
        return users;
    }

    @Override
    protected String formatData(Object data) {
        List<User> userList = (List<User>) data;
        StringBuilder csvData = new StringBuilder();
        csvData.append("姓名,年龄,邮箱\n");
        for (User user : userList) {
            csvData.append(user.getName()).append(",").append(user.getAge()).append(",").append(user.getEmail()).append("\n");
        }
        return csvData.toString();
    }

    @Override
    protected void createFile() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("users.csv"))) {
            // 文件创建操作在BufferedWriter构造函数中完成
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void writeData(String formattedData) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("users.csv"))) {
            writer.write(formattedData);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

再实现订单数据导出为 Excel 文件的子类:

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

public class OrderExcelExporter extends DataExporter {
    private List<Order> orders;

    public OrderExcelExporter(List<Order> orders) {
        this.orders = orders;
    }

    @Override
    protected Object getData() {
        return orders;
    }

    @Override
    protected String formatData(Object data) {
        // 这里为了简单起见,不进行复杂的格式化,直接返回空字符串
        // 实际应用中可以根据需求进行数据格式化
        return "";
    }

    @Override
    protected void createFile() {
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("订单数据");
        try (FileOutputStream fileOut = new FileOutputStream("orders.xlsx")) {
            workbook.write(fileOut);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void writeData(String formattedData) {
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("订单数据");
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue("订单号");
        headerRow.createCell(1).setCellValue("商品名称");
        headerRow.createCell(2).setCellValue("价格");

        List<Order> orderList = (List<Order>) getData();
        int rowNum = 1;
        for (Order order : orderList) {
            Row row = sheet.createRow(rowNum++);
            row.createCell(0).setCellValue(order.getOrderId());
            row.createCell(1).setCellValue(order.getProductName());
            row.createCell(2).setCellValue(order.getPrice());
        }

        try (FileOutputStream fileOut = new FileOutputStream("orders.xlsx")) {
            workbook.write(fileOut);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

最后是测试代码:

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

public class DataExportTest {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        users.add(new User("张三", 25, "zhangsan@example.com"));
        users.add(new User("李四", 30, "lisi@example.com"));

        UserCsvExporter userExporter = new UserCsvExporter(users);
        userExporter.exportData();

        List<Order> orders = new ArrayList<>();
        orders.add(new Order("1001", "笔记本电脑", 5000.0));
        orders.add(new Order("1002", "手机", 3000.0));

        OrderExcelExporter orderExporter = new OrderExcelExporter(orders);
        orderExporter.exportData();
    }
}

class User {
    private String name;
    private int age;
    private String email;

    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getEmail() {
        return email;
    }
}

class Order {
    private String orderId;
    private String productName;
    private double price;

    public Order(String orderId, String productName, double price) {
        this.orderId = orderId;
        this.productName = productName;
        this.price = price;
    }

    public String getOrderId() {
        return orderId;
    }

    public String getProductName() {
        return productName;
    }

    public double getPrice() {
        return price;
    }
}

在这个案例中,DataExporter是抽象类,定义了数据导出的模板方法exportData,包含了获取数据、格式化数据、创建文件和写入数据的基本步骤。UserCsvExporter和OrderExcelExporter是具体的子类,分别实现了用户数据导出为 CSV 文件和订单数据导出为 Excel 文件的具体逻辑。通过模板方法模式,将数据导出的通用流程抽象出来,不同的数据类型和文件格式的导出逻辑在具体子类中实现,提高了代码的复用性和可维护性。当需要新增一种数据导出类型时,只需要创建一个新的子类,实现相应的抽象方法即可,不需要修改已有的导出流程代码。

三、迭代器模式与命令模式

3.1 迭代器模式

迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种顺序访问聚合对象(如集合)中元素的方法,而不需要暴露聚合对象的内部表示。在迭代器模式中,有两个主要角色:

  • 迭代器接口(Iterator):定义了遍历元素所需的方法,如hasNext(判断是否还有下一个元素)、next(返回下一个元素)等。例如:
public interface Iterator<E> {
    boolean hasNext();
    E next();
}
  • 具体迭代器(Concrete Iterator):实现了迭代器接口,负责管理当前遍历的位置,并实现具体的遍历逻辑。比如,对于一个简单的数组集合,实现其迭代器如下:
public class ArrayIterator<E> implements Iterator<E> {
    private E[] array;
    private int index;

    public ArrayIterator(E[] array) {
        this.array = array;
        this.index = 0;
    }

    @Override
    public boolean hasNext() {
        return index < array.length;
    }

    @Override
    public E next() {
        if (hasNext()) {
            return array[index++];
        }
        return null;
    }
}

此外,还有聚合接口(Aggregate)和具体聚合类(Concrete Aggregate)。聚合接口定义了创建迭代器的方法,具体聚合类实现聚合接口,存储元素并提供创建具体迭代器的方法。迭代器模式将遍历逻辑从聚合对象中分离出来,使得聚合对象可以专注于数据存储,而迭代器负责遍历,降低了耦合度,同时也提供了一种统一的方式来遍历不同类型的集合,提高了代码的可维护性和可扩展性。例如在 Java 集合框架中,ArrayList、LinkedList等集合类都实现了迭代器模式,我们可以通过iterator方法获取迭代器,然后使用hasNext和next方法来遍历集合元素,而不需要关心集合内部是如何存储数据的。

3.2 迭代器模式实战案例

展示一个自定义集合迭代器的实现过程。假设我们有一个自定义的MyList集合类,它内部使用数组来存储元素,现在我们要为它实现一个迭代器,以便能够方便地遍历集合中的元素。

首先定义MyList集合类:

public class MyList<E> {
    private E[] elements;
    private int size;

    public MyList() {
        this.elements = (E[]) new Object[10];// 初始容量为10
        this.size = 0;
    }

    public void add(E element) {
        if (size == elements.length) {
            resize();
        }
        elements[size++] = element;
    }

    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
        return elements[index];
    }

    public int size() {
        return size;
    }

    private void resize() {
        int newCapacity = elements.length * 2;
        E[] newElements = (E[]) new Object[newCapacity];
        System.arraycopy(elements, 0, newElements, 0, size);
        elements = newElements;
    }
}

然后定义迭代器接口和具体的迭代器类:

public interface Iterator<E> {
    boolean hasNext();
    E next();
}

public class MyListIterator<E> implements Iterator<E> {
    private MyList<E> myList;
    private int currentIndex;

    public MyListIterator(MyList<E> myList) {
        this.myList = myList;
        this.currentIndex = 0;
    }

    @Override
    public boolean hasNext() {
        return currentIndex < myList.size();
    }

    @Override
    public E next() {
        if (hasNext()) {
            E element = myList.get(currentIndex);
            currentIndex++;
            return element;
        }
        return null;
    }
}

最后是测试代码:

public class MyListTest {
    public static void main(String[] args) {
        MyList<String> myList = new MyList<>();
        myList.add("Apple");
        myList.add("Banana");
        myList.add("Cherry");

        Iterator<String> iterator = new MyListIterator<>(myList);
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }
    }
}

在这个案例中,MyList是自定义的集合类,MyListIterator是为MyList实现的迭代器类。通过迭代器模式,我们将遍历MyList集合的逻辑封装在MyListIterator中,MyList只需要专注于元素的存储和管理。客户端使用迭代器来遍历集合,而不需要了解MyList内部的数组结构和实现细节,提高了代码的封装性和可维护性。如果后续需要修改MyList的内部存储结构,比如从数组改为链表,只需要修改MyList类和MyListIterator类的实现,而客户端使用迭代器遍历集合的代码不需要修改。

3.3 命令模式

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在命令模式中,主要包含以下几个角色:

  • 命令接口(Command):定义执行操作的接口,通常包含一个execute方法。例如:
public interface Command {
    void execute();
}
  • 具体命令类(ConcreteCommand):实现命令接口,负责调用接收者的操作。它持有接收者的引用,并在execute方法中调用接收者的相应方法来完成命令要执行的操作。比如,有一个开灯的命令:
public class LightOnCommand implements Command {
    private Light light;

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

    @Override
    public void execute() {
        light.turnOn();
    }
}
  • 接收者(Receiver):真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。这里的Light类就是接收者:
public class Light {
    public void turnOn() {
        System.out.println("灯打开了");
    }

    public void turnOff() {
        System.out.println("灯关闭了");
    }
}
  • 调用者(Invoker):要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。比如一个遥控器类:
public class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }
}
  • 客户端(Client):创建具体的命令对象,并且设置命令对象的接收者。例如:
public class Client {
    public static void main(String[] args) {
        Light light = new Light();
        Command lightOnCommand = new LightOnCommand(light);

        RemoteControl remoteControl = new RemoteControl();
        remoteControl.setCommand(lightOnCommand);
        remoteControl.pressButton();
    }
}

通过命令模式,将发出命令的责任和执行命令的责任分割开,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。同时,新的命令可以很容易地加入到系统中,也可以比较容易地设计一个组合命令,还能实现请求的撤销、排队等操作。

3.4 命令模式实战案例

以订单操作命令为例,在一个电商系统中,订单操作包括创建订单、取消订单、支付订单等。使用命令模式可以将这些订单操作封装成命令对象,方便进行管理和扩展。

首先定义订单类:

public class Order {
    private int orderId;
    private String status;

    public Order(int orderId) {
        this.orderId = orderId;
        this.status = "未创建";
    }

    public void create() {
        if ("未创建".equals(status)) {
            status = "已创建";
            System.out.println("订单 " + orderId + " 创建成功");
        } else {
            System.out.println("订单 " + orderId + " 状态错误,无法创建");
        }
    }

    public void cancel() {
        if ("已创建".equals(status) || "已支付".equals(status)) {
            status = "已取消";
            System.out.println("订单 " + orderId + " 取消成功");
        } else {
            System.out.println("订单 " + orderId + " 状态错误,无法取消");
        }
    }

    public void pay() {
        if ("已创建".equals(status)) {
            status = "已支付";
            System.out.println("订单 " + orderId + " 支付成功");
        } else {
            System.out.println("订单 " + orderId + " 状态错误,无法支付");
        }
    }
}

然后定义命令接口和具体的命令类:

public interface OrderCommand {
    void execute();
    void undo();
}

public class CreateOrderCommand implements OrderCommand {
    private Order order;

    public CreateOrderCommand(Order order) {
        this.order = order;
    }

    @Override
    public void execute() {
        order.create();
    }

    @Override
    public void undo() {
        order.cancel();
    }
}

public class CancelOrderCommand implements OrderCommand {
    private Order order;

    public CancelOrderCommand(Order order) {
        this.order = order;
    }

    @Override
    public void execute() {
        order.cancel();
    }

    @Override
    public void undo() {
        order.create();
    }
}

public class PayOrderCommand implements OrderCommand {
    private Order order;

    public PayOrderCommand(Order order) {
        this.order = order;
    }

    @Override
    public void execute() {
        order.pay();
    }

    @Override
    public void undo() {
        // 这里简单处理,实际可能需要更复杂逻辑,比如退款操作等
        order.cancel();
    }
}

接着定义订单处理器类,作为调用者:

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

public class OrderProcessor {
    private List<OrderCommand> commandList = new ArrayList<>();

    public void addCommand(OrderCommand command) {
        commandList.add(command);
    }

    public void processCommands() {
        for (OrderCommand command : commandList) {
            command.execute();
        }
        commandList.clear();
    }

    public void undoLastCommand() {
        if (!commandList.isEmpty()) {
            OrderCommand lastCommand = commandList.remove(commandList.size() - 1);
            lastCommand.undo();
        }
    }
}

最后是测试代码:

public class OrderSystemTest {
    public static void main(String[] args) {
        Order order = new Order(1001);

        OrderProcessor processor = new OrderProcessor();
        processor.addCommand(new CreateOrderCommand(order));
        processor.addCommand(new PayOrderCommand(order));

        processor.processCommands();

        processor.undoLastCommand();
    }
}

在这个案例中,Order是订单类,即接收者;CreateOrderCommand、CancelOrderCommand和PayOrderCommand是具体的命令类,实现了OrderCommand接口;OrderProcessor是订单处理器,作为调用者,负责管理和执行命令。通过命令模式,将订单操作封装成命令对象,可以方便地实现订单操作的记录、撤销等功能。比如在OrderProcessor中,可以很容易地扩展功能,如将命令记录到日志中,或者实现命令的排队执行等,而不会影响到具体的订单操作逻辑和其他模块。


网站公告

今日签到

点亮在社区的每一天
去签到