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

文章目录

  • 策略模式:在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 {@Overridepublic void pay(int amount) {System.out.println(amount + " paid with credit/debit card");}
}public class PayPalStrategy implements PaymentStrategy {@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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 {@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic double applyDiscount(double totalAmount) {return totalAmount * (1 - percentage / 100);}
}public class FixedAmountDiscountStrategy implements DiscountStrategy {private double amount;public FixedAmountDiscountStrategy(double amount) {this.amount = amount;}@Overridepublic 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.rhkb.cn/news/400762.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

波涛汹涌的海面:适用于恶劣环境的水冷电阻器

电阻器液体冷却可提升 3.3kV 中压负载&#xff0c;并大幅减少工业和船舶应用中的电阻器占用空间。在起重机、升降机、升降机和输送机等电机驱动应用中&#xff0c;风冷电阻器很常见&#xff0c;但在中压、高功率应用中&#xff0c;液体冷却胜出。 使用 3.3kV 电源运行以转动 5…

vue前端可以完整的显示编辑子级部门,用户管理可以为用户分配角色和部门?

用户和角色是一对多的关系用户和部门是多对多得关系<template><div class="s"><!-- 操作按钮 --><div class="shang"><el-input v-model="searchText" placeholder="请输入搜索关键词" style="width:…

MySQL的InnoDB的页里面存了些什么 --InnoDB存储梳理(三)

文章目录 创建新表页的信息新增一条数据根据页号找数据信息脚本代码py_innodb_page_info根据地址计算页号根据页号计算起始地址 主要介绍表空间索引页里面有哪些内容&#xff0c;数据在表空间文件里面是怎么组织的 创建新表页的信息 CREATE TABLE test8 (id bigint(20) NOT N…

跟着iMeta学做图|ggplot2绘制多个饼图展示菌群物种组成

原始教程链接&#xff1a;https://github.com/iMetaScience/iMetaPlot/tree/main/221017multi-pieplot 写在前面 饼图 (Pie Plot) 在微生物组研究中可以用来展示菌群物种组成&#xff0c;可以起到与堆叠柱状图相同的展示效果。本期我们挑选2022年4月5日刊登在iMeta上的The imp…

服务器安装哪吒面板详细教程

本文长期更新地址&#xff1a; 服务器安装哪吒面板详细教程-星零岁的博客https://blog.0xwl.com/13568.html 注&#xff1a;本文中部分内容源自网络&#xff0c;第四步中部分来自本人曾经文章&#xff1a;云服务器安装配置宝塔面板并安装基础运行环境教程-星零岁的博客 今天来讲…

VGMShield:揭秘视频生成模型滥用的检测与追踪技术

人工智能咨询培训老师叶梓 转载标明出处 视频生成模型&#xff0c;如 Stable Video Diffusion 和 Videocrafter&#xff0c;已经能够生成合理且高分辨率的视频。但这些技术进步也带来了被恶意利用的风险&#xff0c;比如用于制造假新闻或进行政治宣传。因此&#xff0c;来自弗…

前端学习大纲 | 主流前端技术 | 学习路线

需要完整的学习路线的宝子可以点击获取&#xff1a;点击即可获取完整的学习路线 第一阶段&#xff08;页面还原能力&#xff09; HTML5、CSS3、Git 第二阶段&#xff08;专攻 JS 逻辑能力&#xff09; JavaScript 基础、JavaScript 进阶、JavaScript 高级、ES6 第三阶段&a…

Leetcode面试经典150题-125.验证回文串

解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {/**这题目感觉不是算法题&#xff0c;应该是考coding的细节点&#xff0c;比如如何判断两个字符是否equals&#xff08;大小写要equals&#xff09;空格要忽略&#xff0c;感觉就是纯coding&#xff0c;难道是为…

城市信息模型:构建未来智慧城市之基底座

在智慧城市的宏大叙事中&#xff0c;城市信息模型&#xff08;City Information Model, CIM&#xff09;平台如同城市智能的神经中枢&#xff0c;将数据、空间与技术深度融合&#xff0c;为城市规划、管理、服务、居民生活提供了前所未有的洞察与优化途径。CIM平台的构建不仅是…

【鸿蒙学习】HarmonyOS应用开发者基础 - 构建更加丰富的页面之Tabs(三)

学完时间&#xff1a;2024年8月14日 一、前言叨叨 学习HarmonyOS的第六课&#xff0c;人数又成功的降了500名左右&#xff0c;到了3575人了。 本文接上一文章【鸿蒙学习】HarmonyOS应用开发者基础 - 构建更加丰富的页面&#xff08;一&#xff09;&#xff0c;继续记录构建更…

微信小程序预览PDF、H5预览PDF、网页预览PDF,并添加专属文字水印

下载PDF.js 点击PDF.js下载地址 引入预览PDF 文件 // const url new URL("./1.pdf", import.meta.url).href // 在本地项目获取pdf // const url "https://xxxx/05d833041f.pdf" // 在线上链接获取pdf const url query.get(url) // 在地址栏获取pdf c…

【STM32项目】在FreeRtos背景下的实战项目的实现过程(一)

个人主页~ 这篇文章是我亲身经历的&#xff0c;在做完一个项目之后总结的经验&#xff0c;虽然我没有将整个项目给放出来&#xff0c;因为这项目确实也是花了米让导师指导的&#xff0c;但是这个过程对于STM32的实战项目开发都是非常好用的&#xff0c;可以说按照这个过程&…

c++ - 特殊类设计

文章目录 一、设计一个不允许拷贝的类二、设计一个只能在堆上实例对象的类三、设计一个只能在栈上创建对象的类四、设计一个不能被继承的类五、设计一个只能创建一个对象的类&#xff08;单例模式&#xff09; 一、设计一个不允许拷贝的类 1、方法一&#xff1a;将拷贝构造和赋…

多云网络部署存在挑战,F5分布式云应用简化方案解读

伴随着人工智能与云计算领域的深度融合与集中爆发&#xff0c;企业在多云环境中面临着挑战&#xff0c;包括复杂性、成本和风险的增加等……对于高度敏捷企业中的开发团队而言&#xff0c;传统网络的沉重运维成本和低效率将会成为发展的障碍。作为一家提供多云应用安全和应用交…

TcpSocket在切后台后如何保活

1&#xff09;TcpSocket在切后台后如何保活 2&#xff09;Magica Clothes 2插件与Burst编译问题 3&#xff09;粒子拖尾合批失败怎么办 4&#xff09;如何让射线追踪跟随我FPS游戏的十字准星进行移动 这是第398篇UWA技术知识分享的推送&#xff0c;精选了UWA社区的热门话题&…

flink文档刨析

flink任务运行方式 flink任务提交方式有3种 session、per-job、application三种 flink任务运行底座也有三种&#xff0c;Standalone、yarn、k8s 原则上一个flink任务运行的方式由3*39种&#xff0c;但是有些是没法搭配的 yarn&#xff1a;包括yarn-session 、yarn-per-job 、…

uni-app 吸顶方案总结

效果 页面级 uni.pageScrollTo 官方文档&#xff1a;https://uniapp.dcloud.net.cn/api/ui/scroll.html#pagescrollto 原生头部导航 uni.pageScrollTo({selector: #tabs,duration: 300 });(推荐)需要兼容自定义头部导航 <template><view id"demo1" :styl…

Vue框架学习笔记-6

Vue中的路由 Vue中的路由&#xff08;Routing&#xff09;是通过Vue Router这个官方提供的路由管理器来实现的。Vue Router允许你通过不同的URL访问应用中不同的页面&#xff08;组件&#xff09;&#xff0c;而无需重新加载页面。这对于构建单页应用&#xff08;SPA, Single …

【Hadoop】建立圈内组件的宏观认识

01存储02计算03调度04其他05回忆 众多组件们构建了大规模分布式计算和存储平台。本文介绍Hadoop生态圈中各个组件的主要功能和作用&#xff0c;辅助学者理解每个组件的定位和用途&#xff0c;从而建立对圈内组件的宏观认识。梳理清楚HDFS、MapReduce、YARN、Hive、HBase、Spark…

推出 SAM 2:适用于视频和图像的下一代 Meta Segment Anything 模型

继图像元分割模型(SAM) 取得成功之后&#xff0c;我们发布了SAM 2&#xff0c;这是一个用于在图像和视频中实时提示对象分割的统一模型&#xff0c;可实现最先进的性能。 为了秉承我们的开放科学方针&#xff0c;我们通过宽松的 Apache 2.0 许可证共享代码和模型权重。 我们还…