设计模式(下)行为型篇

3、行为型模式

用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。GOF(四人组)书中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等11种行为型模式。

  1. 策略模式 (Strategy Pattern): 定义一系列算法,使它们可以相互替换,而不影响客户端使用。类似于选择不同的旅游路线。
  2. 模板方法模式 (Template Method Pattern): 定义一个算法的骨架,允许子类重写特定步骤。类似于烹饪食谱,其中一些步骤可以根据菜谱进行调整。
  3. 观察者模式 (Observer Pattern): 定义了对象之间的一对多依赖关系,当一个对象改变状态时,其依赖的对象会收到通知。类似于订阅报纸,读者会在有新闻时收到通知。
  4. 迭代器模式 (Iterator Pattern): 提供一种方法来遍历集合对象,而不需要暴露其内部结构。就像使用迭代器在购物清单中遍历商品。
  5. 责任链模式 (Chain of Responsibility Pattern): 将请求的发送者和接收者解耦,通过一条链来处理请求。就像审批流程中不同级别的领导逐步审批。
  6. 命令模式 (Command Pattern): 将请求封装成对象,允许参数化、队列化或记录化请求。类似于点餐,服务员将客人的点餐请求传递给厨房。
  7. 备忘录模式 (Memento Pattern): 捕获对象的内部状态,以便稍后可以将其恢复。就像保存游戏进度,以便在之后继续游戏。
  8. 状态模式 (State Pattern): 允许对象在其内部状态改变时改变其行为。类似于游戏中角色的状态,如生命、死亡等。
  9. 访问者模式 (Visitor Pattern): 在不改变元素类的情况下,定义元素的新操作。类似于博物馆导览员根据展品提供不同的解释。
  10. 中介者模式 (Mediator Pattern): 减少对象之间的直接通信,通过引入中介者来协调交互。类似于团队的领导,协调成员之间的合作。
  11. 解释器模式 (Interpreter Pattern): 定义一个语言的文法,并解析语句。类似于编写解释器来解释特定领域的命令。

3.1、策略模式

策略模式是一种行为型设计模式,它允许在运行时选择一种算法或行为,从一组可互换的算法中进行选择,以达到灵活地改变对象的行为。这种模式将算法从主体中分离出来,使得算法可以独立于使用它的客户端进行变化。策略模式在实现开放/封闭原则(对扩展开放,对修改封闭)方面具有很强的优势,因为可以通过添加新的策略来增强系统功能,而无需修改现有代码。

具体实现

示例:购物折扣策略

假设我们有一个购物系统,需要根据不同的折扣策略计算最终支付金额。系统中有三种折扣策略:无折扣、打九折和满200减50。我们将使用策略模式来实现这个例子。

1. 创建抽象策略类(DiscountStrategy.java):
// 抽象策略类
interface DiscountStrategy {
    double applyDiscount(double amount);
}
2. 创建具体策略类(NoDiscountStrategy.java、TenPercentDiscountStrategy.java、FiftyOffDiscountStrategy.java):
// 无折扣策略
class NoDiscountStrategy implements DiscountStrategy {
    @Override
    public double applyDiscount(double amount) {
        return amount;
    }
}

// 打九折策略
class TenPercentDiscountStrategy implements DiscountStrategy {
    @Override
    public double applyDiscount(double amount) {
        return amount * 0.9;
    }
}

// 满200减50策略
class FiftyOffDiscountStrategy implements DiscountStrategy {
    @Override
    public double applyDiscount(double amount) {
        return amount >= 200 ? amount - 50 : amount;
    }
}
3. 创建环境类(ShoppingCart.java):
class ShoppingCart {
    private DiscountStrategy discountStrategy;

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

    public double calculateTotal(double amount) {
        double discountedAmount = discountStrategy.applyDiscount(amount);
        // 执行其他购物车逻辑...
        return discountedAmount;
    }
}
4. 客户端代码:
public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();
        
        // 选择折扣策略
        DiscountStrategy noDiscount = new NoDiscountStrategy();
        cart.setDiscountStrategy(noDiscount);
        double totalNoDiscount = cart.calculateTotal(100);
        System.out.println("Total with no discount: " + totalNoDiscount);
        
        DiscountStrategy tenPercentDiscount = new TenPercentDiscountStrategy();
        cart.setDiscountStrategy(tenPercentDiscount);
        double totalTenPercentDiscount = cart.calculateTotal(150);
        System.out.println("Total with 10% discount: " + totalTenPercentDiscount);
        
        DiscountStrategy fiftyOffDiscount = new FiftyOffDiscountStrategy();
        cart.setDiscountStrategy(fiftyOffDiscount);
        double totalFiftyOffDiscount = cart.calculateTotal(250);
        System.out.println("Total with $50 off discount: " + totalFiftyOffDiscount);
    }
}

优化案例:

假设我们有一个排序工具类 Sorter,它可以对一个整数数组进行排序。目前该工具只支持冒泡排序,但是我们希望能够随时切换到其他排序算法,比如快速排序。我们可以使用策略模式来实现这一切换。

1. 创建抽象策略类(SortStrategy.java):
// 抽象策略类
interface SortStrategy {
    void sort(int[] array);
}
2. 创建具体策略类(BubbleSortStrategy.java、QuickSortStrategy.java):
// 冒泡排序策略
class BubbleSortStrategy implements SortStrategy {
    @Override
	public void sort(int[] array) {
    	int n = array.length;
    
   		for (int i = 0; i < n - 1; i++) {
        	for (int j = 0; j < n - i - 1; j++) {
            	if (array[j] > array[j + 1]) {
                	// 交换 array[j] 和 array[j + 1] 的位置
                	int temp = array[j];
                	array[j] = array[j + 1];
                	array[j + 1] = temp;
            	}
        	}
    	}
	}

}



// 快速排序策略
class QuickSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        quickSort(array, 0, array.length - 1);
    }

    // 快速排序的实现
    private void quickSort(int[] array, int low, int high) {
        if (low < high) {
            // 找到划分点,使得左边的元素都比划分点小,右边的元素都比划分点大
            int pivotIndex = partition(array, low, high);
            
            // 对划分点左右两侧的子数组进行递归排序
            quickSort(array, low, pivotIndex - 1);
            quickSort(array, pivotIndex + 1, high);
        }
    }

    // 划分函数,将数组划分为比划分点小和大的两部分,并返回划分点的索引
    private int partition(int[] array, int low, int high) {
        int pivot = array[high]; // 选择最后一个元素作为划分点
        int i = low - 1; // i 是小于划分点的部分的最后一个元素的索引

        for (int j = low; j < high; j++) {
            if (array[j] < pivot) {
                i++;
                swap(array, i, j);
            }
        }

        swap(array, i + 1, high); // 将划分点放到正确的位置
        return i + 1; // 返回划分点的索引
    }

    // 交换数组中两个元素的位置
    private void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

3. 创建环境类(Sorter.java):
class Sorter {
    private SortStrategy sortStrategy;

    public void setSortStrategy(SortStrategy sortStrategy) {
        this.sortStrategy = sortStrategy;
    }

    public void performSort(int[] array) {
        sortStrategy.sort(array);
    }
}
4. 客户端代码:
public class Main {
    public static void main(String[] args) {
        Sorter sorter = new Sorter();
        int[] array = {5, 2, 8, 1, 3};
        
        // 使用冒泡排序
        SortStrategy bubbleSort = new BubbleSortStrategy();
        sorter.setSortStrategy(bubbleSort);
        sorter.performSort(array);
        System.out.println("Sorted using bubble sort: " + Arrays.toString(array));
        
        // 使用快速排序
        SortStrategy quickSort = new QuickSortStrategy();
        sorter.setSortStrategy(quickSort);
        sorter.performSort(array);
        System.out.println("Sorted using quick sort: " + Arrays.toString(array));
    }
}

在这个优化案例中,策略模式使得 Sorter 类的排序算法可以轻松地进行切换,而不需要修改排序工具类的代码。这符合开放/封闭原则,提高了代码的可维护性和可扩展性。

总结

①策略模式就是用一个接口抽象策略,再实现其对应的具体策略

②提供一个环境类,成员变量是接口抽象策略,然后根据构造器传入的具体对象,再方法种用对象调用自己的方法,实现的策略效果。

关注点是:在环境类中,通过构造器传入对应的策略实现对象,然后用环境类的对象调用方法,方法种是策略对象调用自己的策略方法。

主要核心是:通过构造器注入策略对象,改变环境对象的方法功能

3.2、模板方法模式

模板方法模式是一种行为设计模式,它定义了一个算法的骨架,将一些步骤的具体实现延迟到子类中。模板方法模式使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。这种模式在一些相似的流程中很有用,可以减少重复代码,提高代码的复用性和扩展性。

具体实现

让我们以咖啡和茶的制作过程为例来演示模板方法模式。在制作咖啡和茶时,存在一些共同的步骤(如烧水、冲泡、倒入杯子等),但是咖啡和茶的某些步骤是不同的(如添加咖啡粉或茶叶)。

首先,我们定义一个抽象类 Beverage 作为模板,其中包含制作饮料的模板方法和共同的步骤:

abstract class Beverage {
    // 模板方法,定义制作饮料的流程
    final void makeBeverage() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    // 共同的步骤
    void boilWater() {
        System.out.println("Boiling water");
    }

    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 需要子类实现的步骤
    abstract void brew();
    abstract void addCondiments();
}

class Coffee extends Beverage {
    // 子类实现咖啡的冲泡方法
    void brew() {
        System.out.println("Dripping Coffee through filter");
    }

    // 子类实现添加咖啡调料方法
    void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }
}

class Tea extends Beverage {
    // 子类实现茶的冲泡方法
    void brew() {
        System.out.println("Steeping the tea");
    }

    // 子类实现添加茶调料方法
    void addCondiments() {
        System.out.println("Adding Lemon");
    }
}

代码说明:

  1. Beverage 是抽象模板类,其中的 makeBeverage 方法定义了制作饮料的流程,包括共同的步骤和需要子类实现的步骤。
  2. CoffeeTea 是具体子类,分别实现了在模板中定义的抽象方法。

优化前的垃圾代码:

假设有一个垃圾代码片段,它在制作咖啡和茶时重复了许多相同的步骤:

class GarbageCode {
    void makeCoffee() {
        System.out.println("Boiling water");
        System.out.println("Dripping Coffee through filter");
        System.out.println("Pouring into cup");
        System.out.println("Adding Sugar and Milk");
    }

    void makeTea() {
        System.out.println("Boiling water");
        System.out.println("Steeping the tea");
        System.out.println("Pouring into cup");
        System.out.println("Adding Lemon");
    }
}

优化后的代码:

使用模板方法模式来优化上述垃圾代码,消除重复步骤:

abstract class BeverageTemplate {
    final void makeBeverage() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    void boilWater() {
        System.out.println("Boiling water");
    }

    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    abstract void brew();
    abstract void addCondiments();
}

class CoffeeTemplate extends BeverageTemplate {
    void brew() {
        System.out.println("Dripping Coffee through filter");
    }

    void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }
}

class TeaTemplate extends BeverageTemplate {
    void brew() {
        System.out.println("Steeping the tea");
    }

    void addCondiments() {
        System.out.println("Adding Lemon");
    }
}

代码说明: 通过使用模板方法模式,我们将共同的步骤提取到模板类中,避免了代码的重复。GarbageCode 类中的 makeCoffeemakeTea 方法已经不再需要,而优化后的代码更加结构化和可维护。每个饮料的制作步骤仍由子类实现,但共同的步骤在抽象类中统一定义。

总结: 模板方法模式允许我们定义一个算法的框架,将共同步骤放在父类中,然后由子类提供具体实现。这有助于减少代码重复,提高代码复用性和可扩展性。在我们的例子中,模板方法模式让我们以一种更优雅的方式制作咖啡和茶,消除了重复代码,使得代码更加清晰易懂。

总结

定义一个抽象模板,包含部分抽象方法,并且有个final方法中调用普通方法和抽象方法形成流程。子类去实现这个抽象模板即可

核心是在一个抽象类(抽象模板)中,共用的方法直接实现,特别方法抽象定义。用final修复方法调用流程。

3.3、观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式的实现方案使得主题(被观察者)和观察者之间的耦合度降低,让它们可以独立地变化。

具体实现

示例:天气预报系统

场景描述

假设我们要实现一个简单的天气预报系统。我们有一个天气站(主题),多个不同的用户(观察者)订阅了该天气站,希望在天气更新时得到通知并显示最新的天气信息。

观察者模式代码实现

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

// 主题接口
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

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

// 具体主题类:天气站
class WeatherStation implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String weather;

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

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

    public void setWeather(String weather) {
        this.weather = weather;
        notifyObservers(); // 通知所有观察者
    }
}

// 具体观察者类:用户
class User implements Observer {
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public void update(String weather) {
        System.out.println(name + " 收到天气更新,当前天气:" + weather);
    }
}

public class ObserverPatternExample {
    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();

        User user1 = new User("小明");
        User user2 = new User("小红");
        User user3 = new User("小刚");

        weatherStation.registerObserver(user1);
        weatherStation.registerObserver(user2);
        weatherStation.registerObserver(user3);

        weatherStation.setWeather("晴天");
        weatherStation.setWeather("下雨");
    }
}

在上面的代码中,我们首先定义了两个接口 SubjectObserver,分别用于主题和观察者的抽象。然后,我们实现了具体的主题类 WeatherStation 和观察者类 User。主题类维护了一个观察者列表,当天气发生变化时,会遍历通知所有的观察者更新。

优化前的垃圾代码示例

假设有一个需求:在某个应用中,当用户登录成功后,需要在不同模块中进行各种不相关的操作,例如发送欢迎邮件、记录登录日志等。原始代码可能会在登录成功的地方直接调用这些操作,导致代码臃肿、耦合度高,难以维护。

public class BadLoginService {
    public void login(String username, String password) {
        // 执行登录逻辑
        boolean loginResult = performLogin(username, password);

        if (loginResult) {
            // 发送欢迎邮件
            sendWelcomeEmail(username);
            
            // 记录登录日志
            logLogin(username);
            
            // ...更多操作
        }
    }

    private boolean performLogin(String username, String password) {
        // 登录逻辑实现
        return true; // 假设登录成功
    }

    private void sendWelcomeEmail(String username) {
        // 发送邮件逻辑
        System.out.println("发送欢迎邮件给 " + username);
    }

    private void logLogin(String username) {
        // 记录日志逻辑
        System.out.println("记录登录日志:" + username);
    }

    // ...更多操作的方法
}

用观察者模式优化的示例代码

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

// 主题接口
interface LoginObserver {
    void onLogin(String username);
}

// 观察者类:发送欢迎邮件
class WelcomeEmailSender implements LoginObserver {
    @Override
    public void onLogin(String username) {
        System.out.println("发送欢迎邮件给 " + username);
    }
}

// 观察者类:记录登录日志
class LoginLogger implements LoginObserver {
    @Override
    public void onLogin(String username) {
        System.out.println("记录登录日志:" + username);
    }
}

// 登录服务类:主题
class LoginService {
    private List<LoginObserver> observers = new ArrayList<>();

    public void addObserver(LoginObserver observer) {
        observers.add(observer);
    }

    public void login(String username, String password) {
        boolean loginResult = performLogin(username, password);

        if (loginResult) {
            notifyObservers(username);
        }
    }

    private boolean performLogin(String username, String password) {
        // 登录逻辑实现
        return true; // 假设登录成功
    }

    private void notifyObservers(String username) {
        for (LoginObserver observer : observers) {
            observer.onLogin(username);
        }
    }
}

public class ObserverPatternLoginExample {
    public static void main(String[] args) {
        LoginService loginService = new LoginService();

        LoginObserver welcomeEmailSender = new WelcomeEmailSender();
        LoginObserver loginLogger = new LoginLogger();

        loginService.addObserver(welcomeEmailSender);
        loginService.addObserver(loginLogger);

        loginService.login("user123", "pass456");
    }
}

在上面的优化后的代码中,我们引入了观察者模式来解耦登录服务中的不同操作。每个操作(发送欢迎邮件、记录登录日志等)都作为一个观察者实现,它们实现了共同的 LoginObserver 接口,并在 onLogin 方法中处理相应

总结

俩个角色

①观察者:需要实现一个观察者接口

②被观察者:成员变量有一个是观察者接口集合,然后有一个方法是遍历这个接口集合,并且用对应的接口实现调用其方法实现。

3.4、迭代器模式

迭代器模式(Iterator Pattern) 是一种行为型设计模式,它提供一种方法来访问一个容器对象中的各个元素,而不暴露该对象的内部结构。通过提供一个迭代器(Iterator)接口,客户端可以顺序访问容器中的元素,而无需关心容器内部的实现细节。

具体实现

以下是迭代器模式的代码实现,以及一个生活中的例子:

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

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

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

// 具体迭代器
class ConcreteIterator<T> implements Iterator<T> {
    private List<T> items;
    private int position = 0;

    public ConcreteIterator(List<T> items) {
        this.items = items;
    }

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

    @Override
    public T next() {
        return items.get(position++);
    }
}

// 具体聚合
class ConcreteAggregate<T> implements Aggregate<T> {
    private List<T> items = new ArrayList<>();

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

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

public class IteratorPatternExample {
    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()) {
            System.out.println(iterator.next());
        }
    }
}

在这个例子中,我们使用迭代器模式来实现了一个简单的迭代器和聚合,用于遍历一个字符串列表中的元素。

现在,让我们看一段需要被优化的垃圾代码,并使用迭代器模式来优化它:

垃圾代码示例:

public class BadCodeExample {
    public static void main(String[] args) {
        String[] items = {"Item 1", "Item 2", "Item 3"};
        for (int i = 0; i < items.length; i++) {
            System.out.println(items[i]);
        }
    }
}

这段代码使用了传统的循环来遍历数组中的元素,但是存在以下问题:

  1. 暴露了数组的内部实现。
  2. 代码不够灵活,如果数据结构改变,遍历逻辑也需要变化。

现在,我们使用迭代器模式来优化这段代码:

优化后的代码示例:

// 迭代器接口和具体迭代器已经在前面的代码示例中给出,这里不再重复定义。

// 具体聚合
class ArrayAggregate<T> implements Aggregate<T> {
    private T[] items;

    public ArrayAggregate(T[] items) {
        this.items = items;
    }

    @Override
    public Iterator<T> createIterator() {
        return new ConcreteIterator<>(Arrays.asList(items));
    }
}

public class IteratorOptimizationExample {
    public static void main(String[] args) {
        String[] items = {"Item 1", "Item 2", "Item 3"};
        ArrayAggregate<String> aggregate = new ArrayAggregate<>(items);

        Iterator<String> iterator = aggregate.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

通过使用迭代器模式,我们将遍历逻辑与具体的数据结构分离开来,提高了代码的可维护性和灵活性。无论数据结构如何变化,遍历代码都保持不变。同时,我们也不需要直接暴露数组的内部实现。这就是迭代器模式的优势所在。

总结

迭代器模式就是要清楚①聚合就是集合或者数组之类的容器。②迭代器实现类就是实现next方法和hashnext方法 ,并在构造器传入聚合数据, 用成员变量存储,再定义一个下表指针。

核心是下一个元素的获取交给迭代器实现类来实现,聚合实现类是将现有的容器放入一个创建迭代类的方法中,返回迭代器对象即可

3.5、责任链模式

责任链模式是一种行为型设计模式,它允许你将请求沿着处理链进行传递,直到有一个处理者能够处理该请求。每个处理者都可以选择将请求处理掉,或者将其传递给下一个处理者。这种模式常常被用于需要按照一定顺序进行处理的场景,例如事件处理、日志记录等。

具体实现

以下是使用Java代码实现责任链模式的示例,假设我们模拟一个报销审批系统。在这个系统中,员工可以提交报销申请,然后报销申请会依次经过不同层级的审批人进行审批。

// 抽象处理者
abstract class Approver {
    protected Approver nextApprover;

    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }

    public abstract void approveExpense(Expense expense);
}

// 具体处理者 - 经理
class Manager extends Approver {
    @Override
    public void approveExpense(Expense expense) {
        if (expense.getAmount() <= 1000) {
            System.out.println("经理审批通过,报销金额:" + expense.getAmount());
        } else if (nextApprover != null) {
            nextApprover.approveExpense(expense);
        }
    }
}

// 具体处理者 - 部门主管
class DepartmentHead extends Approver {
    @Override
    public void approveExpense(Expense expense) {
        if (expense.getAmount() <= 5000) {
            System.out.println("部门主管审批通过,报销金额:" + expense.getAmount());
        } else if (nextApprover != null) {
            nextApprover.approveExpense(expense);
        }
    }
}

// 具体处理者 - 总经理
class CEO extends Approver {
    @Override
    public void approveExpense(Expense expense) {
        if (expense.getAmount() <= 10000) {
            System.out.println("总经理审批通过,报销金额:" + expense.getAmount());
        } else {
            System.out.println("报销金额太大,无法审批!");
        }
    }
}

// 报销申请类
class Expense {
    private double amount;

    public Expense(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }
}

public class ChainOfResponsibilityExample {
    public static void main(String[] args) {
        // 创建处理者链
        Approver manager = new Manager();
        Approver departmentHead = new DepartmentHead();
        Approver ceo = new CEO();

        manager.setNextApprover(departmentHead);
        departmentHead.setNextApprover(ceo);

        // 创建报销申请
        Expense expense1 = new Expense(800);
        Expense expense2 = new Expense(4500);
        Expense expense3 = new Expense(12000);

        // 提交报销申请
        manager.approveExpense(expense1);
        manager.approveExpense(expense2);
        manager.approveExpense(expense3);
    }
}

在上面的代码中,我们实现了一个简单的报销审批系统,其中每个具体处理者根据报销金额的大小来判断是否可以审批,如果无法审批则传递给下一个处理者。

优化垃圾代码示例

假设我们有一段垃圾代码,用于处理日志输出,但是每个地方都在重复进行日志输出的操作:

public class LogHandler {
    public void logInfo(String message) {
        System.out.println("Info: " + message);
    }

    public void logWarning(String message) {
        System.out.println("Warning: " + message);
    }

    public void logError(String message) {
        System.out.println("Error: " + message);
    }
    
    // ... 更多日志类型和方法
}

这段代码存在重复的日志输出逻辑,可以通过责任链模式来优化:

abstract class Logger {
    protected Logger nextLogger;

    public void setNextLogger(Logger nextLogger) {
        this.nextLogger = nextLogger;
    }

    public abstract void logMessage(LogLevel level, String message);
}

class InfoLogger extends Logger {
    @Override
    public void logMessage(LogLevel level, String message) {
        if (level == LogLevel.INFO) {
            System.out.println("Info: " + message);
        } else if (nextLogger != null) {
            nextLogger.logMessage(level, message);
        }
    }
}

class WarningLogger extends Logger {
    @Override
    public void logMessage(LogLevel level, String message) {
        if (level == LogLevel.WARNING) {
            System.out.println("Warning: " + message);
        } else if (nextLogger != null) {
            nextLogger.logMessage(level, message);
        }
    }
}

class ErrorLogger extends Logger {
    @Override
    public void logMessage(LogLevel level, String message) {
        if (level == LogLevel.ERROR) {
            System.out.println("Error: " + message);
        } else {
            System.out.println("Invalid log level");
        }
    }
}

enum LogLevel {
    INFO, WARNING, ERROR
}

public class LoggerChainExample {
    public static void main(String[] args) {
        // 创建处理者链
        Logger infoLogger = new InfoLogger();
        Logger warningLogger = new WarningLogger();
        Logger errorLogger = new ErrorLogger();

        infoLogger.setNextLogger(warningLogger);
        warningLogger.setNextLogger(errorLogger);

        // 使用责任链处理日志
        infoLogger.logMessage(LogLevel.INFO, "This is an info message.");
        infoLogger.logMessage(LogLevel.WARNING, "This is a warning message.");
        infoLogger.logMessage(LogLevel.ERROR, "This is an error message.");
    }
}

在上述代码中,我们使用责任链模式来优化了原本的日志处理代码。不同的日志级别交由不同的处理者来处理,从而避免了重复的日志输出逻辑。如果当前处理者无法处理某个日志级别,它会将请求传递给下一个处理者,直到找到合适的处理者为止。

通过责任链模式,我们能够有效地组织和分离不同处理逻辑,使代码更加清晰和可维护。这种模式能够将请求发送者和接收者解耦,同时在处理请求时提供了更大的灵活性。

总结

责任链就是主打一个链路,也就是从最低的级别开始,不满足就走下一个级别。

需要一个处理者抽象类然后有一个成员变量是指向下一级别的自己的抽象类,接着通过一个方法判断,让不满足条件的用下一个级别的对象调用当前方法即可,递归调用(自己调用自己,逐级向上)

3.6、命令模式

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而使得请求的发出者和接收者解耦。在命令模式中,通过将请求封装成一个对象,可以实现对请求的参数化、队列化、记录化和撤销等操作,从而提供更大的灵活性和可扩展性。

命令模式的主要参与者包括:

  • 命令(Command):声明执行操作的接口,通常包括一个 execute 方法。
  • 具体命令(Concrete Command):实现命令接口,将一个接收者对象绑定于一个动作。
  • 接收者(Receiver):执行命令的对象。
  • 调用者/请求者(Invoker):要求命令执行的对象,持有命令对象的引用。
  • 客户端(Client):创建具体命令对象并设置其接收者。

具体实现

生活例子

假设我们有一个遥控器(Invoker),它有一些按钮,每个按钮都可以执行一个特定的操作,如开灯、关灯、调节音量等。我们可以用命令模式来实现这个场景。

首先,定义命令接口:

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

然后,实现具体命令:

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

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

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

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

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

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

接着,定义接收者(灯):

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

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

创建遥控器(Invoker):

// 遥控器
class RemoteControl {
    private Command command;

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

    public void pressButton() {
        command.execute();
    }
}

客户端代码:

public class Client {
    public static void main(String[] args) {
        // 创建灯对象和命令对象
        Light light = new Light();
        Command lightOnCommand = new LightOnCommand(light);
        Command lightOffCommand = new LightOffCommand(light);

        // 创建遥控器并设置命令
        RemoteControl remoteControl = new RemoteControl();
        remoteControl.setCommand(lightOnCommand);

        // 按下遥控器按钮
        remoteControl.pressButton();  // 输出:灯已经打开

        // 设置不同的命令并再次按下按钮
        remoteControl.setCommand(lightOffCommand);
        remoteControl.pressButton();  // 输出:灯已经关闭
    }
}

优化前的垃圾代码

假设有以下垃圾代码,实现了一个简单的计算器,但代码重复冗长:

class Calculator {
    public void add(int x, int y) {
        System.out.println(x + y);
    }

    public void subtract(int x, int y) {
        System.out.println(x - y);
    }

    public void multiply(int x, int y) {
        System.out.println(x * y);
    }

    public void divide(int x, int y) {
        System.out.println(x / y);
    }
}

public class Client {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        calculator.add(5, 3);
        calculator.subtract(10, 4);
        calculator.multiply(6, 2);
        calculator.divide(8, 2);
    }
}

用命令模式优化垃圾代码

现在,我们将使用命令模式优化上述垃圾代码。首先,我们将命令模式的结构应用于这个场景:

  1. 命令接口:Command
  2. 具体命令类:AddCommandSubtractCommandMultiplyCommandDivideCommand
  3. 接收者类:Calculator
  4. 调用者类:CalculatorInvoker
// 优化后的命令模式实现

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

// 具体命令:加法命令
class AddCommand implements Command {
    private Calculator calculator;
    private int x, y;

    public AddCommand(Calculator calculator, int x, int y) {
        this.calculator = calculator;
        this.x = x;
        this.y = y;
    }

    public void execute() {
        calculator.add(x, y);
    }
}

// 具体命令:减法命令
class SubtractCommand implements Command {
    private Calculator calculator;
    private int x, y;

    public SubtractCommand(Calculator calculator, int x, int y) {
        this.calculator = calculator;
        this.x = x;
        this.y = y;
    }

    public void execute() {
        calculator.subtract(x, y);
    }
}

// 具体命令:乘法命令
class MultiplyCommand implements Command {
    private Calculator calculator;
    private int x, y;

    public MultiplyCommand(Calculator calculator, int x, int y) {
        this.calculator = calculator;
        this.x = x;
        this.y = y;
    }

    public void execute() {
        calculator.multiply(x, y);
    }
}

// 具体命令:除法命令
class DivideCommand implements Command {
    private Calculator calculator;
    private int x, y;

    public DivideCommand(Calculator calculator, int x, int y) {
        this.calculator = calculator;
        this.x = x;
        this.y = y;
    }

    public void execute() {
        calculator.divide(x, y);
    }
}

// 接收者:计算器
class Calculator {
    public void add(int x, int y) {
        System.out.println(x + y);
    }

    public void subtract(int x, int y) {
        System.out.println(x - y);
    }

    public void multiply(int x, int y) {
        System.out.println(x * y);
    }

    public void divide(int x, int y) {
        System.out.println(x / y);
    }
}

// 调用者:计算器调用者
class CalculatorInvoker {
    private Command command;

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

    public void performCalculation() {
        command.execute();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        Command addCommand = new AddCommand(calculator, 5, 3);
        Command subtractCommand = new SubtractCommand(calculator, 10, 4);
        Command multiplyCommand = new MultiplyCommand(calculator, 6, 2);
        Command divideCommand = new DivideCommand(calculator, 8, 2);

        CalculatorInvoker invoker = new CalculatorInvoker();

        invoker.setCommand(addCommand);
        invoker.performCalculation();

        invoker.setCommand(subtractCommand);
        invoker.performCalculation();

        invoker.setCommand(multiplyCommand);
        invoker.performCalculation();

        invoker.setCommand(divideCommand);
        invoker.performCalculation();
    }
}

通过命令模式,我们将不同的计算操作封装成命令对象,从而将调用者与具体的计算操作解耦。这提高了代码的可维护性、扩展性和重用性。同时,我们还可以实现撤销、日志记录等功能,而无需修改现有代码。这种设计模式更好地组织了代码,并遵循了单一职责原则和开闭原则。

总结

命令模式主要是让调用者和接收者解耦,把接收者放入具体命令中,并让调用者塞入具体命令,再调用自己的命令的方法

3.7、备忘录模式

备忘录模式用于捕捉一个对象的内部状态,以便在之后可以将该对象恢复到先前的状态。这在需要撤销、回滚或历史记录功能的情况下特别有用。

具体实现

下面我将分别展示黑箱和白箱备忘录模式的实现,以及详细的解释和代码示例。然后,我会提供一个需要优化的垃圾代码示例,并用备忘录模式来优化它。

备忘录模式 - 黑箱实现

在黑箱备忘录模式中,备忘录对象会封装在原发器(Originator)中,外部无法访问备忘录的内部状态。

生活例子

假设我们有一个文本编辑器,我们希望能够保存和恢复文本的历史状态。

// 备忘录类
class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

// 原发器类
class TextEditor {
    private String text;

    public void write(String text) {
        this.text = text;
    }

    public Memento save() {
        return new Memento(text);
    }

    public void restore(Memento memento) {
        text = memento.getState();
    }

    public void printText() {
        System.out.println("Current Text: " + text);
    }
}

// 负责人类
class Caretaker {
    private Memento memento;

    public void saveMemento(Memento memento) {
        this.memento = memento;
    }

    public Memento getMemento() {
        return memento;
    }
}

public class Main {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        Caretaker caretaker = new Caretaker();

        editor.write("Hello, World!");
        editor.printText();

        // 保存当前状态
        caretaker.saveMemento(editor.save());

        editor.write("Updated Text");
        editor.printText();

        // 恢复之前的状态
        editor.restore(caretaker.getMemento());
        editor.printText();
    }
}

备忘录模式 - 白箱实现

在白箱备忘录模式中,备忘录对象的内部状态可以被外部访问。

生活例子

假设我们有一个游戏中的角色类,我们希望能够保存和恢复角色的状态。

 // 原发器类
class GameCharacter {
    private String state;

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

    public String getState() {
        return state;
    }

    public Memento save() {
        return new CharacterMemento(state);
    }

    public void restore(Memento memento) {
        state = memento.getState();
    }
}

// 具体备忘录类
class CharacterMemento implements Memento {
    private String state;

    public CharacterMemento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

public class Main {
    public static void main(String[] args) {
        GameCharacter character = new GameCharacter();

        character.setState("Level 1");
        System.out.println("Character State: " + character.getState());

        // 保存当前状态
        Memento memento = character.save();

        character.setState("Level 2");
        System.out.println("Character State: " + character.getState());

        // 恢复之前的状态
        character.restore(memento);
        System.out.println("Character State: " + character.getState());
    }
}

优化垃圾代码

假设我们有以下垃圾代码,需要使用备忘录模式进行优化。代码中存在一个数字的历史状态需要被记录和恢复。

public class BadCode {
    private int number;

    public void setNumber(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public void printNumber() {
        System.out.println("Current Number: " + number);
    }

    // ...其他不相关的方法...
}

这段代码的问题在于它没有良好的历史记录和状态恢复机制。我们可以使用备忘录模式来解决这个问题。

// 备忘录类
class NumberMemento {
    private int number;

    public NumberMemento(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }
}

// 原发器类
class NumberManager {
    private int number;

    public void setNumber(int number) {
        this.number = number;
    }

    public int getNumber() {
        return number;
    }

    public NumberMemento save() {
        return new NumberMemento(number);
    }

    public void restore(NumberMemento memento) {
        number = memento.getNumber();
    }

    public void printNumber() {
        System.out.println("Current Number: " + number);
    }
}

public class Main {
    public static void main(String[] args) {
        NumberManager manager = new NumberManager();

        manager.setNumber(42);
        manager.printNumber();

        // 保存当前状态
        NumberMemento memento = manager.save();

        manager.setNumber(99);
        manager.printNumber();

        // 恢复之前的状态
        manager.restore(memento);
        manager.printNumber();
    }
}

这样,我们使用备忘录模式将数字的历史状态进行了记录和恢复,使代码更具有可维护性和可扩展性。

备忘录模式的核心思想在于将对象的状态进行封装,从而实现状态的保存和恢复。无论是黑箱还是白箱备忘录模式,都能在不影响对象本身的情况下实现这一目标。这种模式在需要实现撤销、回滚、历史记录等功能时非常有用,帮助我们更好地管理对象的状态。

总结

备忘录模式有白箱和黑箱俩种实现方式,目标都是把一个对象的状态用另外一个类的对象来存储下来

  1. 白箱模式:保存对象的那个类内部状态可以被外部访问。

    只有一个额外的类存储了这个对象信息

  2. 黑箱模式:保存对象会封装在原发器(Originator)中,外部无法访问备忘录的内部状态。

    用内部内或者负责人类来存储这个需要被保存的对象信息,相当于俩个地方存储了这个对象信息

3.8、状态模式

状态模式是一种行为型设计模式,它允许对象在内部状态改变时改变其行为。通过将不同的状态封装成独立的类,并使得这些状态类共同实现一个共同的接口,对象可以在不同状态之间切换,从而实现不同状态下的不同行为。

具体实现

示例解释: 假设我们要实现一个简单的音频播放器应用,其中有三种播放状态:播放、暂停和停止。使用状态模式,我们可以使播放器对象根据不同状态来执行不同的操作,例如在播放状态下播放音乐,暂停状态下暂停音乐,停止状态下停止音乐。

// 状态接口
interface State {
    void play();
    void pause();
    void stop();
}

// 具体状态类:播放状态
class PlayState implements State {
    @Override
    public void play() {
        System.out.println("音乐正在播放中");
    }

    @Override
    public void pause() {
        System.out.println("音乐暂停");
    }

    @Override
    public void stop() {
        System.out.println("音乐停止");
    }
}

// 具体状态类:暂停状态
class PauseState implements State {
    @Override
    public void play() {
        System.out.println("继续播放音乐");
    }

    @Override
    public void pause() {
        System.out.println("音乐已经暂停");
    }

    @Override
    public void stop() {
        System.out.println("音乐停止");
    }
}

// 具体状态类:停止状态
class StopState implements State {
    @Override
    public void play() {
        System.out.println("开始播放音乐");
    }

    @Override
    public void pause() {
        System.out.println("音乐已经停止,无法暂停");
    }

    @Override
    public void stop() {
        System.out.println("音乐已经停止");
    }
}

// 环境类:音频播放器
class AudioPlayer {
    private State currentState;

    public AudioPlayer() {
        currentState = new StopState();
    }

    public void setState(State state) {
        currentState = state;
    }

    public void play() {
        currentState.play();
    }

    public void pause() {
        currentState.pause();
    }

    public void stop() {
        currentState.stop();
    }
}

public class Main {
    public static void main(String[] args) {
        AudioPlayer player = new AudioPlayer();

        player.play();  // 输出:开始播放音乐
        player.pause(); // 输出:音乐已经停止,无法暂停

        player.setState(new PlayState());
        player.play();  // 输出:音乐正在播放中
        player.pause(); // 输出:音乐暂停
        player.stop();  // 输出:音乐停止
    }
}

垃圾代码示例: 以下是一个简单的垃圾代码示例,展示了一个状态切换的逻辑。这段代码中,不同的状态逻辑混杂在一起,难以维护和扩展。

public class GarbageCode {
    private int state = 0;

    public void process() {
        if (state == 0) {
            System.out.println("执行步骤 0");
            state = 1;
        } else if (state == 1) {
            System.out.println("执行步骤 1");
            state = 2;
        } else if (state == 2) {
            System.out.println("执行步骤 2");
            state = 0;
        }
    }

    public static void main(String[] args) {
        GarbageCode garbageCode = new GarbageCode();

        garbageCode.process(); // 输出:执行步骤 0
        garbageCode.process(); // 输出:执行步骤 1
        garbageCode.process(); // 输出:执行步骤 2
    }
}

优化后的代码: 下面是使用状态模式优化后的代码,将状态切换的逻辑分离到不同的状态类中,提高了代码的可维护性和扩展性。

interface State {
    void process();
}

class State0 implements State {
    @Override
    public void process() {
        System.out.println("执行步骤 0");
    }
}

class State1 implements State {
    @Override
    public void process() {
        System.out.println("执行步骤 1");
    }
}

class State2 implements State {
    @Override
    public void process() {
        System.out.println("执行步骤 2");
    }
}

class OptimizedCode {
    private State currentState = new State0();

    public void setState(State state) {
        currentState = state;
    }

    public void process() {
        currentState.process();
    }

    public static void main(String[] args) {
        OptimizedCode optimizedCode = new OptimizedCode();

        optimizedCode.process(); // 输出:执行步骤 0
        optimizedCode.setState(new State1());
        optimizedCode.process(); // 输出:执行步骤 1
        optimizedCode.setState(new State2());
        optimizedCode.process(); // 输出:执行步骤 2
    }
}

在优化后的代码中,每个状态都被封装成了独立的类,通过切换不同的状态类来实现不同的行为。这样做提高了代码的可读性、可维护性和可扩展性,使得新的状态可以更方便地添加进来,而不会影响到已有的逻辑。这就是状态模式的优势所在。

总结

状态模式主要正对根据某个状态有大量的switch和if...else的场景进行优化,提高代码的可扩展性,可读性

就是定义一个抽象类表示状态,然后具体的状态实现它。接着有一个环境类引入状态抽象,并提供一些列的操作方法。

3.9、访问者模式

访问者模式是一种行为型设计模式,用于在不修改现有对象结构的情况下,定义一组新的操作。该模式将数据结构与操作分离,使得可以在不同的操作下,访问同一个数据结构中的元素。这种分离使得新增操作变得更加容易,同时也增加了系统的灵活性。

具体实现

让我们通过一个生活例子来说明访问者模式。假设我们有一个动物园,里面有各种不同类型的动物:狗、猫和鸟。我们希望实现一个访问者模式,以便能够对这些动物进行不同的操作,比如喂食、进行健康检查等。

首先,我们需要定义一个动物类层次结构,包括狗、猫和鸟:

// 动物类
interface Animal {
    void accept(AnimalVisitor visitor);
}

// 具体动物类
class Dog implements Animal {
    @Override
    public void accept(AnimalVisitor visitor) {
        visitor.visit(this);
    }
}

class Cat implements Animal {
    @Override
    public void accept(AnimalVisitor visitor) {
        visitor.visit(this);
    }
}

class Bird implements Animal {
    @Override
    public void accept(AnimalVisitor visitor) {
        visitor.visit(this);
    }
}

然后,我们定义一个访问者接口,其中包含了不同操作的方法:

// 访问者接口
interface AnimalVisitor {
    void visit(Dog dog);
    void visit(Cat cat);
    void visit(Bird bird);
}

接下来,我们实现具体的访问者类,比如进行喂食和健康检查:

// 具体访问者类
class FeedingVisitor implements AnimalVisitor {
    @Override
    public void visit(Dog dog) {
        System.out.println("给狗狗喂食");
    }

    @Override
    public void visit(Cat cat) {
        System.out.println("给猫咪喂食");
    }

    @Override
    public void visit(Bird bird) {
        System.out.println("给小鸟喂食");
    }
}

class HealthCheckVisitor implements AnimalVisitor {
    @Override
    public void visit(Dog dog) {
        System.out.println("对狗狗进行健康检查");
    }

    @Override
    public void visit(Cat cat) {
        System.out.println("对猫咪进行健康检查");
    }

    @Override
    public void visit(Bird bird) {
        System.out.println("对小鸟进行健康检查");
    }
}

现在,我们可以创建一个动物园,并让不同的访问者对动物进行操作:

public class Zoo {
    public static void main(String[] args) {
        Animal[] animals = { new Dog(), new Cat(), new Bird() };

        AnimalVisitor feedingVisitor = new FeedingVisitor();
        AnimalVisitor healthCheckVisitor = new HealthCheckVisitor();

        for (Animal animal : animals) {
            animal.accept(feedingVisitor);
            animal.accept(healthCheckVisitor);
        }
    }
}

在这个示例中,访问者模式允许我们定义不同的操作(喂食和健康检查),并将这些操作应用于相同的数据结构(动物类层次结构),同时避免了修改动物类的代码。

垃圾代码示例

假设我们有一个用于计算不同图形面积的类,但每次新增一种图形都需要修改计算方法,违反了开闭原则。

class AreaCalculator {
    double calculateCircleArea(Circle circle) {
        return Math.PI * Math.pow(circle.getRadius(), 2);
    }

    double calculateRectangleArea(Rectangle rectangle) {
        return rectangle.getWidth() * rectangle.getHeight();
    }
}

class Circle {
    double radius;

    public double getRadius() {
        return radius;
    }
}

class Rectangle {
    double width;
    double height;

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }
}

这里我们可以看到,每次新增一种图形,都需要修改AreaCalculator类,违背了开闭原则。

优化使用访问者模式

我们可以使用访问者模式来优化上述代码。首先,定义图形类层次结构,并为每种图形实现accept方法:

interface Shape {
    void accept(ShapeVisitor visitor);
}

class Circle implements Shape {
    double radius;

    public double getRadius() {
        return radius;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

class Rectangle implements Shape {
    double width;
    double height;

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

然后,定义访问者接口和具体访问者类来执行不同的操作:

interface ShapeVisitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
}

class AreaCalculator implements ShapeVisitor {
    double totalArea = 0;

    @Override
    public void visit(Circle circle) {
        totalArea += Math.PI * Math.pow(circle.getRadius(), 2);
    }

    @Override
    public void visit(Rectangle rectangle) {
        totalArea += rectangle.getWidth() * rectangle.getHeight();
    }

    public double getTotalArea() {
        return totalArea;
    }
}

现在,我们可以创建不同的图形,并使用访问者模式计算总面积,而无需修改现有代码:

public class Main {
    public static void main(String[] args) {
        Shape[] shapes = { new Circle(), new Rectangle() };

        AreaCalculator areaCalculator = new AreaCalculator();
        for (Shape shape : shapes) {
            shape.accept(areaCalculator);
        }

        System.out.println("Total area: " + areaCalculator.getTotalArea());
    }
}

通过使用访问者模式,我们将图形类和计算逻辑分离,可以轻松地新增新的图形类型而无需修改现有的计算逻辑,遵循了开闭原则。

总结

多个访问者基于抽象类,然后每个访问者的实现,里面的方法是根据元素的不同入参实现方法重载

3.10、中介者模式

中介模式(Mediator Pattern) 是一种行为型设计模式,旨在减少对象之间的直接耦合,通过引入一个中介对象来协调其他对象之间的交互。这个中介对象负责处理对象间的通信,从而使对象之间的关系更加松散,易于维护和扩展。中介模式适用于系统中多个对象之间的相互交互复杂,需要降低耦合度的情况。

具体实现

让我们以一个机场调度系统为例来说明中介模式。假设一个机场内有多架飞机,每架飞机都需要与塔台、地面交通控制以及其他飞机进行通信。如果每架飞机直接与所有其他对象通信,代码将变得混乱且难以维护。这时,我们可以使用中介模式来优化这种情况。

中介模式代码实现与解释

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

// 中介者接口
interface ATCMediator {
    void sendMessage(String message, Aircraft aircraft);
}

// 具体中介者
class AirTrafficControl implements ATCMediator {
    private List<Aircraft> aircraftList;

    public AirTrafficControl() {
        this.aircraftList = new ArrayList<>();
    }

    @Override
    public void sendMessage(String message, Aircraft aircraft) {
        for (Aircraft a : aircraftList) {
            if (a != aircraft) {
                a.receiveMessage(message);
            }
        }
    }

    public void addAircraft(Aircraft aircraft) {
        aircraftList.add(aircraft);
    }
}

// 飞机抽象类
abstract class Aircraft {
    protected ATCMediator mediator;

    public Aircraft(ATCMediator mediator) {
        this.mediator = mediator;
    }

    public abstract void sendMessage(String message);
    public abstract void receiveMessage(String message);
}

// 具体飞机类
class Airplane extends Aircraft {
    public Airplane(ATCMediator mediator) {
        super(mediator);
    }

    @Override
    public void sendMessage(String message) {
        mediator.sendMessage(message, this);
    }

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

public class MediatorPatternExample {
    public static void main(String[] args) {
        AirTrafficControl atc = new AirTrafficControl();
        Aircraft airplane1 = new Airplane(atc);
        Aircraft airplane2 = new Airplane(atc);

        atc.addAircraft(airplane1);
        atc.addAircraft(airplane2);

        airplane1.sendMessage("Flight 123 requesting landing clearance.");
        airplane2.sendMessage("Copy, Flight 123. You are clear to land.");
    }
}

在这个示例中,AirTrafficControl 充当中介者,负责管理飞机之间的通信。每架飞机继承自抽象类 Aircraft,并通过 ATCMediator 接口与其他飞机进行通信。

  • ATCMediator:中介者接口,定义了发送消息的方法。
  • AirTrafficControl:具体中介者,维护飞机列表,实现消息的广播。
  • Aircraft:飞机抽象类,包含一个中介者引用和抽象的发送/接收消息方法。
  • Airplane:具体飞机类,实现发送和接收消息方法。

垃圾代码示例

假设有一个图书管理系统,其中多个类之间直接耦合,代码难以维护:

class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public void requestBook(String bookTitle) {
        Library.checkoutBook(bookTitle, this);
    }

    public void receiveBook(String bookTitle) {
        System.out.println(name + " received the book: " + bookTitle);
    }
}

class Library {
    public static void checkoutBook(String bookTitle, User user) {
        Book book = BookDatabase.getBook(bookTitle);
        if (book != null && !book.isCheckedOut()) {
            book.setCheckedOut(true);
            System.out.println("Book " + bookTitle + " checked out by " + user.getName());
            user.receiveBook(bookTitle);
        }
    }
}

class Book {
    private String title;
    private boolean checkedOut;

    public Book(String title) {
        this.title = title;
        this.checkedOut = false;
    }

    public boolean isCheckedOut() {
        return checkedOut;
    }

    public void setCheckedOut(boolean checkedOut) {
        this.checkedOut = checkedOut;
    }
}

class BookDatabase {
    private static List<Book> books = new ArrayList<>();

    public static Book getBook(String title) {
        for (Book book : books) {
            if (book.getTitle().equals(title)) {
                return book;
            }
        }
        return null;
    }
}

这段代码存在直接的耦合,如果需求变更,可能需要修改多处代码。现在,让我们使用中介模式进行优化。

优化后的代码示例

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

// 中介者接口
interface LibraryMediator {
    void checkoutBook(String bookTitle, User user);
}

// 具体中介者
class LibrarySystem implements LibraryMediator {
    private List<Book> books;

    public LibrarySystem() {
        this.books = new ArrayList<>();
    }

    @Override
    public void checkoutBook(String bookTitle, User user) {
        Book book = findBook(bookTitle);
        if (book != null && !book.isCheckedOut()) {
            book.setCheckedOut(true);
            System.out.println("Book " + bookTitle + " checked out by " + user.getName());
            user.receiveBook(bookTitle);
        }
    }

    public void addBook(Book book) {
        books.add(book);
    }

    private Book findBook(String title) {
        for (Book book : books) {
            if (book.getTitle().equals(title)) {
                return book;
            }
        }
        return null;
    }
}

class User {
    private String name;
    private LibraryMediator mediator;

    public User(String name, LibraryMediator mediator) {
        this.name = name;
        this.mediator = mediator;
    }

    public String getName() {
        return name;
    }

    public void requestBook(String bookTitle) {
        mediator.checkoutBook(bookTitle, this);
    }

    public void receiveBook(String bookTitle) {
        System.out.println(name + " received the book: " + bookTitle);
    }
}

class Book {
    private String title;
    private boolean checkedOut;

    public Book(String title) {
        this.title = title;
        this.checkedOut = false;
    }

    public String getTitle() {
        return title;
    }

    public boolean isCheckedOut() {
        return checkedOut;
    }

    public void setCheckedOut(boolean checkedOut) {
        this.checkedOut = checkedOut;
    }
}

public class MediatorPatternOptimizedExample {
    public static void main(String[] args) {
        LibrarySystem library = new LibrarySystem();
        User user1 = new User("Alice", library);
        User user2 = new User("Bob", library);

        library.addBook(new Book("Design Patterns"));
        library.addBook(new Book("Clean Code"));

        user1.requestBook("Design Patterns");
        user2.requestBook("Clean Code");
    }
}

在优化后的代码中,中介者 LibrarySystem 管理了图书的借阅过程,User 类通过传入中介者实现了与图书的交互。这样,系统的耦合性降低,易于扩展和维护。

总结

中介模式通过引入一个中介者对象,协调多个对象之间的交互,降低了对象之间的直接耦合,使系统更具可维护性和扩展性。在示例中,我们以机场调度系统和图书管理系统为例,分别实现了中介模式的代码示例。在图书管理系统中,中介模式帮助我们优化了原本耦合度高的代码,使系统更加灵活和可维护。

同事类引入中介抽象,中介抽象引入同事抽象。比如(用户包含图书馆,图书馆包含书籍)| (租户包含中介,户主包含中介,中介包含租户和户主)

同事与同事通过中介联系

3.11、解释器模式

设计模式是一种在软件设计中常见问题的解决方案模板,可以帮助开发者更好地组织和优化代码。其中的解释器模式(Interpreter Pattern)主要用于定义一个语言的文法规则,并提供一个解释器来解释该语言中的句子。

具体实现

模式含义: 解释器模式用于定义一个语言的文法,并为该语言创建一个解释器。它允许你按照特定的规则解释一个语言中的句子。这在处理自定义的领域特定语言(Domain Specific Language,DSL)或规则引擎中非常有用。通过该模式,可以将复杂的问题分解为一系列简单的表达式,然后使用解释器逐个解释这些表达式。

生活例子: 假设我们需要实现一个简单的数学表达式解释器,它能够解释并计算像 "2 + 3 - 1" 这样的数学表达式。

代码实现: 首先,我们需要定义表达式的抽象语法树节点(Abstract Syntax Tree,AST)类:

// 抽象表达式节点
interface Expression {
    int interpret();
}

// 数字表达式节点
class NumberExpression implements Expression {
    private int value;
    
    public NumberExpression(int value) {
        this.value = value;
    }
    
    public int interpret() {
        return value;
    }
}

// 加法表达式节点
class AddExpression implements Expression {
    private Expression left;
    private Expression right;
    
    public AddExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
    
    public int interpret() {
        return left.interpret() + right.interpret();
    }
}

// 减法表达式节点
class SubtractExpression implements Expression {
    private Expression left;
    private Expression right;
    
    public SubtractExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
    
    public int interpret() {
        return left.interpret() - right.interpret();
    }
}

现在,我们可以使用解释器模式来解释数学表达式:

public class InterpreterPatternExample {
    public static void main(String[] args) {
        // 构建表达式:2 + 3 - 1
        Expression expression = new SubtractExpression(
            new AddExpression(new NumberExpression(2), new NumberExpression(3)),
            new NumberExpression(1)
        );
        
        // 解释并计算表达式的值
        int result = expression.interpret();
        System.out.println("Expression result: " + result);  // 输出:Expression result: 4
    }
}

优化垃圾代码示例: 假设我们有以下垃圾代码,用于处理一组数字,并根据一些规则计算总和:

public class GarbageCode {
    public static int processNumbers(List<Integer> numbers) {
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return sum;
    }
}

这段代码很简单,但如果我们希望能够根据不同的规则进行不同的计算,解释器模式可以帮助我们更灵活地处理这些情况。

优化后的代码: 我们可以使用解释器模式来优化上述垃圾代码,使其支持不同的计算规则:

首先,定义抽象表达式接口和具体表达式类:

interface CalculationExpression {
    int interpret(List<Integer> numbers);
}

class SumExpression implements CalculationExpression {
    public int interpret(List<Integer> numbers) {
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return sum;
    }
}

class ProductExpression implements CalculationExpression {
    public int interpret(List<Integer> numbers) {
        int product = 1;
        for (int num : numbers) {
            product *= num;
        }
        return product;
    }
}

然后,使用解释器模式来计算不同规则下的结果:

public class InterpreterPatternOptimization {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(2, 3, 4);
        
        CalculationExpression sumExpression = new SumExpression();
        int sumResult = sumExpression.interpret(numbers);
        System.out.println("Sum result: " + sumResult);  // 输出:Sum result: 9
        
        CalculationExpression productExpression = new ProductExpression();
        int productResult = productExpression.interpret(numbers);
        System.out.println("Product result: " + productResult);  // 输出:Product result: 24
    }
}

通过使用解释器模式,我们可以轻松地扩展代码,支持不同的计算规则,同时保持代码的可维护性和灵活性。


设计模式(下)行为型篇
http://101.126.22.188:9090//2023/10/31/1698765120139
作者
不是王总
发布于
2023年10月31日
更新于
2023年10月31日
许可协议