【策略模式】设计模式系列:在Java中实现灵活的行为选择(实战指南)

news2025/2/28 17:19:53

文章目录

  • 策略模式:在Java中实现灵活的行为选择
    • 引言
    • 1. 策略模式的组成
      • 1.1 抽象策略 (Strategy)
      • 1.2 具体策略 (Concrete Strategy)
      • 1.3 上下文 (Context)
      • 1.4 UML类图和时序图
    • 2. 策略模式在Java中的实现
      • 步骤一:定义抽象策略接口
      • 步骤二:创建具体策略实现
      • 步骤三:构建上下文
      • 步骤四:使用策略模式
    • 3. 策略模式的应用案例
      • 案例一:排序算法的选择
      • 案例二:支付方式的选择
      • 案例三:优惠券计算
    • 4. 策略模式的优缺点
    • 5. 策略模式的设计原则
      • 5.1 开闭原则
      • 5.2 单一职责原则
      • 5.3 里氏替换原则
    • 6. 策略模式与相关模式对比
      • 6.1 与工厂模式的比较
      • 6.2 与状态模式的区别
      • 6.3 与其他行为型模式的关系
    • 7. 进阶主题
      • 7.1 使用Java Lambda表达式简化策略模式
      • 7.2 策略模式与依赖注入框架的结合
      • 7.3 策略模式与Spring框架的集成
    • 8. 常见问题解答
      • 8.1 如何选择合适的策略?
      • 8.2 如何处理策略之间的依赖关系?
    • 9. 性能考量
      • 9.1 性能影响分析
      • 9.2 性能优化技巧
    • 10. 最佳实践
      • 10.1 何时使用策略模式
      • 10.2 如何避免过度使用策略模式
      • 10.3 避免常见陷阱
    • 11. 总结

策略模式:在Java中实现灵活的行为选择


引言

1. 行为型模式简介
设计模式是一系列经过验证的解决方案,旨在解决软件开发过程中常见的问题。行为型模式是一种设计模式类别,它关注于封装系统中的算法和行为。这些模式通常涉及对象之间的交互方式,以及如何分配职责。通过使用行为型模式,开发者能够更好地组织代码并提高其可维护性。

2. 策略模式的意义
在软件工程中,策略模式是一种行为型设计模式,它使我们能够在运行时选择算法或行为。这种模式允许算法独立于使用它的客户端,并且可以轻松地替换不同的算法实现而不改变客户端代码。策略模式的主要优势在于它提高了代码的灵活性和可扩展性。

3. 策略模式的定义
定义与目的
策略模式定义了一个算法族,分别封装起来,让它们之间可以互相替换。此模式使得算法的变化不会影响到使用算法的客户。简单来说,策略模式允许我们在运行时更改算法或行为。

适用场景

  • 当一个系统应该在其内部动态地选择从多个算法或行为中的一种时。
  • 当一个算法的不同实现应当独立于使用该算法的客户时。
  • 当一个算法需要提供多种变体时。
  • 当控制在一个或多个特定的对象上执行算法比控制算法自身更重要时。

1. 策略模式的组成

1.1 抽象策略 (Strategy)

抽象策略定义了所有支持的算法的公共接口。这意味着客户端可以使用相同的接口来调用不同的算法实现。抽象策略通常是一个接口或抽象类,它声明了所有具体策略类都必须实现的方法。

示例代码:

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

1.2 具体策略 (Concrete Strategy)

具体策略实现了抽象策略接口,并提供了实际的算法实现。每一个具体策略都代表了一种算法或行为。客户端可以通过配置不同的具体策略来决定使用哪种算法。

示例代码:

public class CreditCardStrategy implements PaymentStrategy {
    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }
}

public class PayPalStrategy implements PaymentStrategy {
    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using PayPal.");
    }
}

1.3 上下文 (Context)

上下文定义了客户端如何使用策略。它维护一个对策略对象的引用,并且通常包含一个方法来设置这个引用。这样,客户端可以通过设置不同的策略对象来改变其行为。

示例代码:

public class ShoppingCart {
    private PaymentStrategy paymentStrategy;

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

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

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

1.4 UML类图和时序图

在这里插入图片描述
在这里插入图片描述


2. 策略模式在Java中的实现

步骤一:定义抽象策略接口

首先,我们需要定义一个抽象策略接口,它将作为所有具体策略的公共接口。在这个例子中,我们将以一个简单的支付系统为例,其中支付策略是可选的,比如使用信用卡、PayPal等。

示例代码:

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

步骤二:创建具体策略实现

接着,我们需要创建几个具体的策略实现类,这些类将实现上述定义的抽象策略接口。我们将创建两个具体的支付策略:CreditCardStrategyPayPalStrategy

示例代码:

public class CreditCardStrategy implements PaymentStrategy {
    private String name;
    private String cardNumber;
    private String cvv;
    private String expiryDate;

    public CreditCardStrategy(String name, String cardNumber, String cvv, String expiryDate) {
        this.name = name;
        this.cardNumber = cardNumber;
        this.cvv = cvv;
        this.expiryDate = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }
}

public class PayPalStrategy implements PaymentStrategy {
    private String emailId;
    private String password;

    public PayPalStrategy(String emailId, String password) {
        this.emailId = emailId;
        this.password = password;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using PayPal.");
    }
}

步骤三:构建上下文

现在我们需要创建一个上下文类,它将持有对策略对象的引用。上下文类将负责调用具体策略对象的方法。在我们的例子中,ShoppingCart 类将作为上下文。

示例代码:

public class ShoppingCart {
    private List<Item> items;
    private PaymentStrategy paymentStrategy;

    public ShoppingCart() {
        items = new ArrayList<>();
    }

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

    public void removeItem(Item item) {
        items.remove(item);
    }

    public int calculateTotal() {
        return items.stream().mapToInt(Item::getPrice).sum();
    }

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

    public void checkout() {
        int amount = calculateTotal();
        paymentStrategy.pay(amount);
    }
}

class Item {
    private String name;
    private int price;

    public Item(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public int getPrice() {
        return price;
    }
}

步骤四:使用策略模式

最后一步是编写客户端代码来演示如何使用策略模式。客户端将创建一个购物车实例,添加一些商品,然后选择一种支付策略进行结账。

客户端代码示例:

public class ShoppingCartClient {
    public static void main(String[] args) {
        // 创建购物车
        ShoppingCart cart = new ShoppingCart();

        // 添加商品
        cart.addItem(new Item("Apple", 10));
        cart.addItem(new Item("Banana", 20));

        // 设置支付策略
        PaymentStrategy strategy = new CreditCardStrategy("John Doe", "1234567890123456", "123", "12/25");
        cart.setPaymentStrategy(strategy);

        // 结账
        cart.checkout();

        // 更改支付策略
        strategy = new PayPalStrategy("john.doe@example.com", "mysecretpassword");
        cart.setPaymentStrategy(strategy);

        // 再次结账
        cart.checkout();
    }
}

这段代码展示了如何使用策略模式来实现支付功能。用户可以选择使用信用卡或PayPal支付,而购物车本身不需要关心具体的支付方式是如何实现的。这种方式使得系统更加灵活,易于扩展和维护。


3. 策略模式的应用案例

案例一:排序算法的选择

在这个案例中,我们将使用策略模式来实现不同的排序算法,如冒泡排序、快速排序等。这将允许我们在运行时选择最适合当前数据集的排序算法。

示例代码:

步骤一:定义抽象策略接口

public interface SortStrategy {
    void sort(int[] array);
}

步骤二:创建具体策略实现

public class BubbleSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        boolean swapped;
        for (int i = 0; i < array.length - 1; i++) {
            swapped = false;
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    swapped = true;
                }
            }
            if (!swapped) break;
        }
    }
}

public class QuickSortStrategy implements SortStrategy {
    public void sort(int[] array) {
        quickSort(array, 0, array.length - 1);
    }

    private void quickSort(int[] array, int low, int high) {
        if (low < high) {
            int pi = partition(array, low, high);
            quickSort(array, low, pi - 1);
            quickSort(array, pi + 1, high);
        }
    }

    private int partition(int[] array, int low, int high) {
        int pivot = array[high];
        int i = (low - 1);
        for (int j = low; j < high; j++) {
            if (array[j] <= pivot) {
                i++;
                int temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
        int temp = array[i + 1];
        array[i + 1] = array[high];
        array[high] = temp;
        return i + 1;
    }
}

步骤三:构建上下文

public class SortingContext {
    private SortStrategy sortStrategy;

    public SortingContext(SortStrategy sortStrategy) {
        this.sortStrategy = sortStrategy;
    }

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

    public void sortArray(int[] array) {
        sortStrategy.sort(array);
    }
}

步骤四:使用策略模式

public class SortingClient {
    public static void main(String[] args) {
        int[] data = {10, 5, 8, 3, 7, 9, 1, 2};

        // 创建上下文
        SortingContext context = new SortingContext(new BubbleSortStrategy());

        // 排序
        context.sortArray(data);
        System.out.println("Sorted using Bubble Sort: " + Arrays.toString(data));

        // 更换策略
        context.setSortStrategy(new QuickSortStrategy());

        // 重新排序
        context.sortArray(data);
        System.out.println("Sorted using Quick Sort: " + Arrays.toString(data));
    }
}

案例二:支付方式的选择

我们已经在这个系列的前面部分介绍了支付方式的选择,这里再次简要回顾一下。

步骤一:定义抽象策略接口

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

步骤二:创建具体策略实现

public class CreditCardStrategy implements PaymentStrategy {
    private String name;
    private String cardNumber;
    private String cvv;
    private String expiryDate;

    public CreditCardStrategy(String name, String cardNumber, String cvv, String expiryDate) {
        this.name = name;
        this.cardNumber = cardNumber;
        this.cvv = cvv;
        this.expiryDate = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }
}

public class PayPalStrategy implements PaymentStrategy {
    private String emailId;
    private String password;

    public PayPalStrategy(String emailId, String password) {
        this.emailId = emailId;
        this.password = password;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using PayPal.");
    }
}

步骤三:构建上下文

public class ShoppingCart {
    private PaymentStrategy paymentStrategy;

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

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

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

步骤四:使用策略模式

public class ShoppingCartClient {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart(new CreditCardStrategy("John Doe", "1234567890123456", "123", "12/25"));
        cart.checkout(100);
        cart.setPaymentStrategy(new PayPalStrategy("john.doe@example.com", "password"));
        cart.checkout(100);
    }
}

案例三:优惠券计算

假设我们需要为一个电子商务平台实现一个优惠券系统,其中可以有不同的优惠券类型,例如百分比折扣、固定金额折扣等。我们将使用策略模式来实现这个系统。

步骤一:定义抽象策略接口

public interface DiscountStrategy {
    double applyDiscount(double totalAmount);
}

步骤二:创建具体策略实现

public class PercentageDiscountStrategy implements DiscountStrategy {
    private double percentage;

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

    @Override
    public double applyDiscount(double totalAmount) {
        return totalAmount * (1 - percentage / 100);
    }
}

public class FixedAmountDiscountStrategy implements DiscountStrategy {
    private double amount;

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

    @Override
    public double applyDiscount(double totalAmount) {
        return Math.max(totalAmount - amount, 0);
    }
}

步骤三:构建上下文

public class ShoppingCart {
    private DiscountStrategy discountStrategy;

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

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

    public double calculateTotal(double totalAmount) {
        return discountStrategy.applyDiscount(totalAmount);
    }
}

步骤四:使用策略模式

public class ShoppingCartClient {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart(new PercentageDiscountStrategy(10));
        double total = 1000.0;
        double discountedTotal = cart.calculateTotal(total);
        System.out.println("Total after 10% discount: " + discountedTotal);

        cart.setDiscountStrategy(new FixedAmountDiscountStrategy(50));
        discountedTotal = cart.calculateTotal(total);
        System.out.println("Total after $50 discount: " + discountedTotal);
    }
}

这些案例展示了策略模式在不同场景下的应用,可以帮助读者更好地理解如何在实际项目中使用策略模式。


4. 策略模式的优缺点

1. 优点
(1)易于扩展

  • 策略模式允许我们在不修改现有代码的情况下增加新的策略。这意味着我们可以轻松地添加新的算法实现,而无需修改那些已经存在的代码。

(2)遵循开闭原则

  • 根据开闭原则(Open-Closed Principle),软件实体应该是对扩展开放的,对修改关闭的。策略模式很好地遵循了这一原则,因为我们可以添加新的策略实现而不需要改变现有代码。

2. 缺点
(1)增加了对象数量

  • 对于每一种策略,都需要创建一个新的类。这可能会导致类的数量增加,特别是在需要很多不同策略的情况下。

(2)客户端需要了解所有策略类

  • 虽然策略模式允许客户端在运行时选择策略,但它也要求客户端知道所有可用的策略类。如果策略类太多,这可能会变得复杂。

5. 策略模式的设计原则

5.1 开闭原则

1. 定义

  • 开闭原则指出,软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着我们可以轻松地添加新功能,而无需修改现有的代码。

2. 应用

  • 在策略模式中,我们可以添加新的策略实现而不需要修改现有的策略接口或上下文类。这种特性使得系统更加灵活,易于维护。

5.2 单一职责原则

1. 定义

  • 单一职责原则(Single Responsibility Principle)指出,一个类应该只有一个引起它变化的原因。这意味着一个类应该专注于完成一项任务。

2. 应用

  • 在策略模式中,每个具体策略类只负责实现一个特定的算法或行为。这种分离使得代码更加清晰,每个类都有明确的目的。

5.3 里氏替换原则

1. 定义

  • 里氏替换原则(Liskov Substitution Principle)指出,子类必须能够替换掉它们的父类,并且程序的行为不会因此发生变化。

2. 应用

  • 在策略模式中,所有具体策略类都是抽象策略接口的子类。这意味着任何接受策略接口的地方都可以接受任何具体策略类的实例,而不会影响程序的行为。

6. 策略模式与相关模式对比

6.1 与工厂模式的比较

  • 工厂模式:工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。工厂模式隐藏了对象创建的细节,并且允许系统独立于如何创建、组合或表示那些对象。
  • 策略模式:策略模式是一种行为型设计模式,它允许在运行时更改算法或行为。
  • 比较:工厂模式可以用来创建策略模式中的具体策略对象。例如,在策略模式中,我们可以使用工厂模式来创建具体的策略实例,而不是直接在客户端代码中实例化策略对象。这可以进一步降低客户端与具体策略实现之间的耦合度。

6.2 与状态模式的区别

  • 状态模式:状态模式允许对象在其内部状态改变时改变其行为。状态模式允许一个对象看起来像是改变了它的类。
  • 策略模式:策略模式允许在运行时更改算法或行为。它将一组相关的行为封装在一起,以便可以在运行时选择其中一个。
  • 区别
    • 状态模式适用于对象的行为随其内部状态改变的情况,而策略模式适用于对象的行为取决于外部输入或环境。
    • 状态模式通常用于当对象的状态改变时,其行为也随之改变的情况。策略模式则用于在运行时根据需要选择不同的行为。

6.3 与其他行为型模式的关系

  • 命令模式:命令模式将请求封装成对象,从而使你能够用不同的请求来参数化客户端,以及支持可撤销的操作。
  • 观察者模式:观察者模式定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。
  • 模板方法模式:模板方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
  • 关系:策略模式与其他行为型模式在设计上有所不同,但它们可以一起使用来实现更复杂的系统行为。例如,策略模式可以与观察者模式结合使用,以实现在不同策略下不同的行为变化通知机制。

7. 进阶主题

7.1 使用Java Lambda表达式简化策略模式

Java 8引入了Lambda表达式,这是一种简洁的方式来定义匿名函数。Lambda表达式可以用来简化策略模式的实现,尤其是当我们不需要创建单独的类来实现策略接口时。

示例代码:

步骤一:定义抽象策略接口

@FunctionalInterface
public interface SortStrategy {
    void sort(int[] array);
}

步骤二:创建具体策略实现

public class SortingClient {
    public static void main(String[] args) {
        int[] data = {10, 5, 8, 3, 7, 9, 1, 2};

        // 使用Lambda表达式定义策略
        SortStrategy bubbleSortStrategy = (array) -> {
            boolean swapped;
            for (int i = 0; i < array.length - 1; i++) {
                swapped = false;
                for (int j = 0; j < array.length - 1 - i; j++) {
                    if (array[j] > array[j + 1]) {
                        int temp = array[j];
                        array[j] = array[j + 1];
                        array[j + 1] = temp;
                        swapped = true;
                    }
                }
                if (!swapped) break;
            }
        };

        SortStrategy quickSortStrategy = (array) -> {
            quickSort(array, 0, array.length - 1);
        };

        // 使用策略
        SortingContext context = new SortingContext(bubbleSortStrategy);
        context.sortArray(data);
        System.out.println("Sorted using Bubble Sort: " + Arrays.toString(data));

        context.setSortStrategy(quickSortStrategy);
        context.sortArray(data);
        System.out.println("Sorted using Quick Sort: " + Arrays.toString(data));
    }

    private static void quickSort(int[] array, int low, int high) {
        if (low < high) {
            int pi = partition(array, low, high);
            quickSort(array, low, pi - 1);
            quickSort(array, pi + 1, high);
        }
    }

    private static int partition(int[] array, int low, int high) {
        int pivot = array[high];
        int i = (low - 1);
        for (int j = low; j < high; j++) {
            if (array[j] <= pivot) {
                i++;
                int temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
        int temp = array[i + 1];
        array[i + 1] = array[high];
        array[high] = temp;
        return i + 1;
    }
}

步骤三:构建上下文

public class SortingContext {
    private SortStrategy sortStrategy;

    public SortingContext(SortStrategy sortStrategy) {
        this.sortStrategy = sortStrategy;
    }

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

    public void sortArray(int[] array) {
        sortStrategy.sort(array);
    }
}

7.2 策略模式与依赖注入框架的结合

依赖注入框架(如Spring)可以帮助管理策略模式中的依赖关系,从而减少代码耦合度。我们可以使用Spring框架来配置和管理策略对象。

示例代码:

Spring配置文件 (applicationContext.xml):

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="bubbleSortStrategy" class="com.example.strategy.BubbleSortStrategy"/>
    <bean id="quickSortStrategy" class="com.example.strategy.QuickSortStrategy"/>

    <bean id="sortingContext" class="com.example.strategy.SortingContext">
        <constructor-arg ref="bubbleSortStrategy"/>
    </bean>
</beans>

客户端代码示例:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SortingClient {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 获取上下文
        SortingContext sortingContext = (SortingContext) context.getBean("sortingContext");

        // 使用默认策略
        sortingContext.sortArray(new int[]{10, 5, 8, 3, 7, 9, 1, 2});
        System.out.println("Sorted using default strategy: " + Arrays.toString(new int[]{10, 5, 8, 3, 7, 9, 1, 2}));

        // 更换策略
        SortStrategy quickSortStrategy = (SortStrategy) context.getBean("quickSortStrategy");
        sortingContext.setSortStrategy(quickSortStrategy);
        sortingContext.sortArray(new int[]{10, 5, 8, 3, 7, 9, 1, 2});
        System.out.println("Sorted using Quick Sort: " + Arrays.toString(new int[]{10, 5, 8, 3, 7, 9, 1, 2}));
    }
}

7.3 策略模式与Spring框架的集成

在Spring框架中,我们可以利用Spring的Bean工厂来配置和管理策略对象,从而简化策略模式的实现。

Spring配置文件 (applicationContext.xml):

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="creditCardStrategy" class="com.example.strategy.CreditCardStrategy"/>
    <bean id="paypalStrategy" class="com.example.strategy.PayPalStrategy"/>

    <bean id="shoppingCart" class="com.example.strategy.ShoppingCart">
        <constructor-arg ref="creditCardStrategy"/>
    </bean>
</beans>

客户端代码示例:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ShoppingCartClient {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 获取购物车上下文
        ShoppingCart shoppingCart = (ShoppingCart) context.getBean("shoppingCart");

        // 使用默认策略
        shoppingCart.checkout(100);
        System.out.println("Paid using default strategy.");

        // 更换策略
        PaymentStrategy paypalStrategy = (PaymentStrategy) context.getBean("paypalStrategy");
        shoppingCart.setPaymentStrategy(paypalStrategy);
        shoppingCart.checkout(100);
        System.out.println("Paid using PayPal.");
    }
}

8. 常见问题解答

8.1 如何选择合适的策略?

  • 评估需求:首先确定应用程序的需求,考虑哪种策略最能满足当前的需求。
  • 性能考量:考虑策略的性能影响,选择性能最优的策略。
  • 扩展性和维护性:选择易于扩展和维护的策略。

8.2 如何处理策略之间的依赖关系?

  • 策略独立:确保每个策略实现都是独立的,尽量避免策略之间的相互依赖。
  • 使用依赖注入:如果确实需要依赖关系,可以使用依赖注入框架来管理这些依赖。

9. 性能考量

9.1 性能影响分析

  • 对象创建成本:由于策略模式增加了对象的数量,每次创建策略对象可能会带来额外的开销。
  • 调用成本:策略模式可能会引入额外的接口调用,这可能会影响性能。
  • 策略选择:策略选择的成本也需要考虑,特别是当策略的数量很大时。

9.2 性能优化技巧

  • 缓存策略对象:对于不变的策略,可以考虑缓存策略对象以减少重复创建的成本。
  • 使用静态工厂方法:使用静态工厂方法来创建策略对象,这样可以避免直接使用new关键字创建对象带来的开销。
  • 策略池:对于经常使用的策略,可以考虑使用策略池来复用已有的策略对象。

10. 最佳实践

10.1 何时使用策略模式

  • 算法选择:当需要在运行时选择算法时。
  • 行为切换:当需要在运行时改变对象的行为时。

10.2 如何避免过度使用策略模式

  • 限制策略数量:避免为每个小变化创建策略,只在真正需要的时候使用策略模式。
  • 考虑其他模式:如果策略模式不适合当前的问题,可以考虑其他设计模式。

10.3 避免常见陷阱

  • 过度设计:不要为了使用策略模式而使用策略模式。
  • 依赖过多:确保策略之间的依赖关系最小化。

11. 总结

策略模式的关键点回顾

  • 定义:策略模式定义了一系列的算法,把它们一个个封装起来,并且使它们可相互替换。
  • 组成部分:包括抽象策略、具体策略和上下文。
  • 优点:易于扩展、遵循开闭原则。
  • 缺点:增加了对象数量、客户端需要了解所有策略类。
  • 设计原则:开闭原则、单一职责原则、里氏替换原则。
  • 应用场景:排序算法选择、支付方式选择、优惠券计算等。
  • 进阶话题:使用Lambda表达式简化策略模式、与依赖注入框架结合、与Spring框架集成。

本文详细介绍了23种设计模式的基础知识,帮助读者快速掌握设计模式的核心概念,并找到适合实际应用的具体模式:
【设计模式入门】设计模式全解析:23种经典模式介绍与评级指南(设计师必备)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2040461.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[LitCTF 2024]exx

输入任意账号密码进行抓包 考查xxe漏洞 我们加入xxe语句并让它回显我们要它会显的东西&#xff1a;先来读取一下用户名和密码 我们可以看到&#xff0c;它已经读取了服务器下的账号密码文件&#xff0c;接着我们直接读取根目录下的flag文件。通常情况下flag文件的位置一般就根…

CompletableFuture——异步编程艺术

目录 1、CompletableFuture是什么&#xff1f; 2、CompletableFuture和Future、CompletionStage的关系&#xff1f; 3、CompletableFuture常用方法 3.1、 创建 CompletableFuture 实例 3.2、完成时触发thenApply、thenAccept、thenRun 3.3、组合多个 CompletableFuture 3…

C语言中的栈

一、栈的定义&#xff1a; 就是只能表的一端操作的顺序表或链表&#xff0c;允许操作的那一端成为栈顶元素&#xff0c;与之相对应的另一端称为栈底元素。 我们向栈里存入元素称为压栈&#xff0c;即最先放入的元素存放在栈底&#xff0c;最后放入的元素存放在栈顶。 我们将…

怿星科技与您相约——2024 Testing Expo

汽车测试及质量监控博览会(中国)Testing Expo China-Automotive 怿星科技展位路线 届时欢迎莅临2057号展台&#xff01;

OpenCV图像滤波(16)应用分离式滤波器函数sepFilter2D()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 对图像应用分离式线性滤波器。 该函数对图像应用分离式线性滤波器。首先&#xff0c;src 的每一行都用 1D 内核 kernelX 进行滤波。然后&#x…

爬虫 Web Js 逆向:RPC 远程调用获取加密参数(1)WebSocket 协议介绍

RPC (Remote Procedure Call) 是远程调用的意思。 在 Js 逆向时&#xff0c;本地可以和浏览器以服务端和客户端的形式通过 WebSocket 协议进行 RPC 通信&#xff0c;这样可以直接调用浏览器中的一些函数方法&#xff0c;不必去在意函数具体的执行逻辑&#xff0c;可以省去大量…

计算机网络面试题汇总

文章目录 计算机网络基础计算机网络体系结构(网络分层模型)OSI 七层模型是什么?每一层的作用是什么?TCP/IP 四层模型是什么?每一层的作用是什么?五层体系结构以及对应的协议为什么网络要分层,分层的好处?常见网络协议有哪些,每一层常见协议有哪些?应用层有哪些常见的协…

【网编】——tcp编程

tcp流程 服务器 头文件&#xff1a; #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <errno.h> #include<stdio.h> #include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ #…

链接Mysql 报错connection errors; unblock with ‘mysqladmin flush-hosts‘错误的解决方法!亲测有效!

文章目录 前言一、使用 mysqladmin flush-hosts 命令解锁 IP 地址二、增加 max_connect_errors 参数三、检查连接错误的原因 前言 今天正常的对各大的测试服进行重启的时候发现每台服务器都启动失败&#xff01;查看日志发现每台服务器都报一下的错误 java.sql.SQLException:…

分布式光伏管理系统具有什么功能?有推荐吗?

1、项目进度管理 分布式光伏管理系统能够全面管理项目的进度&#xff0c;从初步沟通、收资踏勘、设计、施工到并网发电的全流程。系统通过可视化的项目进度管理工具&#xff0c;展示每一步的完成情况&#xff0c;包括员工跟进记录、关键节点时间等&#xff0c;帮助管理者从宏观…

windows下php安装kafka

下载zookeeper Kafka 依赖 Zookeeper 进行分布式协调&#xff0c;所以需要下载Zookeeper &#xff0c;当然你也可以使用kafka包里自带的一个默认配置的 Zookeeper。这里我们单独下载一个 访问Zookeeper官方下载页面在页面中找到最新的稳定版本&#xff0c;点击相应的下载链接…

Datawhale X 魔搭 AI夏令营第四期-魔搭生图task2学习笔记

精读代码 1.环境安装 !pip install simple-aesthetics-predictor!pip install -v -e data-juicer!pip uninstall pytorch-lightning -y !pip install peft lightning pandas torchvision!pip install -e DiffSynth-Studio 开始要先把代码中需要使用到的包、库用pip命令安装。…

Fomepay和Fomecard是不是跑路了?

最近几天&#xff0c;fomepay跑路的消息&#xff0c;很多人知道了&#xff0c;fomepay客服繁忙&#xff0c;也无法提现。更多的是&#xff0c;很多人刚充值&#xff0c;才看到跑路的消息&#xff0c;实在惨啊&#xff01; 而现在&#xff0c;如果之前没有登录过fomepay的话&…

安卓TV入门项目

android studio创建tv项目 下载android studio点此下载 配置环境变量&#xff1a; d盘新增Android文件夹&#xff0c;创建android-avd和android-sdk文件夹 环境变量名称&#xff1a;ANDROID_HOME 环境变量值&#xff1a;D:\Android\android-sdk 环境变量名称&#xff1a;ANDRO…

深入理解 Spring 三级缓存:解决单例 Bean 循环依赖的利器

目录 一、什么是循环依赖&#xff1f; 二、关于传说中的三级缓存 1.基本概念&#xff1a; 2.三级缓存是哪三级&#xff1f; 3.【举个例子】那三级缓存是怎么解决上述代码例子中的A、B互相依赖呢&#xff1f; 详细过程&#xff1a;&#xff08;理解用&#xff09; 简约版…

【专题】2024年7月人工智能AI行业报告合集汇总PDF分享(附原数据表)

原文链接:https://tecdat.cn/?p37350 随着人工智能技术的飞速发展&#xff0c;AI已经成为当今时代的重要驱动力。本报告将聚焦于人工智能AI行业的最新动态&#xff0c;涵盖客户服务、体验营销、资产管理以及国产AI大模型应用等多个领域。通过深入研究和分析&#xff0c;我们…

【Python学习-UI界面】PyQt5 小部件5-QCheckBox

样式如下: 当将QCheckBox对象添加到父窗口时&#xff0c;文本标签之前会出现一个矩形框。 和QRadioButton一样&#xff0c;它也是一个可选择的按钮。 它通常用于用户被要求选择一个或多个可用选项的场景。 不同于单选按钮&#xff0c;复选框默认情况下不是互斥的。 为了限制…

Golang 与 Java:编程语言比较及如何选择

Golang 与 Java&#xff1a;哪种语言更好&#xff1f;我们的详细比较指南涵盖了语法、性能和流行度方面的主要差异&#xff0c;以帮助您做出决定。 在规划项目时&#xff0c;有许多编程语言可供选择。但一开始就选择正确的语言是成功启动或交付的关键。选择错误的语言&#xff…

用R语言进行数据类型的检查和基础转换

下面内容摘录自《R 语言与数据科学的终极指南》专栏文章的部分内容&#xff0c;每篇文章都在 5000 字以上&#xff0c;质量平均分高达 94 分&#xff0c;看全文请点击下面链接&#xff1a; 4章8节&#xff1a;用R做数据重塑&#xff0c;行列命名和数据类型转换-CSDN博客 欢迎…

servlet的执行顺序

执行的时候Tomcat先初始化 然后调用 server 根据server来回调请求方式下面会追入源码解释 package com.haogu.servlet;import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.…