目录

策略模式:软件开发的 “瑞士军刀”

一、什么是策略模式

(一)定义与概念

(二)策略模式的角色

二、策略模式的应用场景

(一)电商平台的支付方式选择

(二)游戏开发中的角色行为

(三)数据处理中的算法选择

三、策略模式的代码实现

(一)以支付系统为例

(二)代码分析与优化

四、策略模式的优缺点

(一)优点

(二)缺点

五、策略模式与其他设计模式的关系

(一)与工厂模式结合

(二)与状态模式的区别

六、总结与展望


策略模式:软件开发的 “瑞士军刀”

        在软件开发的广阔领域中,我们常常会遇到这样的情况:解决同一个问题存在多种方法,而且在不同的场景下,需要动态地选择最合适的方法。这就好比我们出行时,根据距离、时间、预算等因素,会选择不同的交通工具,如步行、骑自行车、坐公交、乘地铁或者开车。在编程世界里,策略模式就扮演着这样一个智能的 “出行规划师” 角色,它为我们提供了一种优雅而灵活的方式来处理多种算法和行为。

        想象一下,你正在开发一个电商系统,其中的订单支付功能需要支持多种支付方式,如信用卡支付、支付宝支付、微信支付等。如果不使用任何设计模式,你可能会写出一个包含大量if - else语句的方法来处理不同的支付逻辑:

public class PaymentService {

public void pay(String paymentType, double amount) {

if ("creditCard".equals(paymentType)) {

// 信用卡支付逻辑

System.out.println("使用信用卡支付:" + amount);

} else if ("alipay".equals(paymentType)) {

// 支付宝支付逻辑

System.out.println("使用支付宝支付:" + amount);

} else if ("wechatPay".equals(paymentType)) {

// 微信支付逻辑

System.out.println("使用微信支付:" + amount);

} else {

System.out.println("不支持的支付方式");

}

}

}

        这样的代码虽然能实现基本功能,但存在诸多问题。首先,代码的可维护性差,如果要新增一种支付方式,就需要在这个if - else语句块中添加新的条件分支,这不仅增加了代码的复杂度,还容易引入错误。其次,代码的扩展性也不好,每次修改支付逻辑都需要直接修改这个方法,违反了软件开发中的开闭原则(对扩展开放,对修改关闭)。

        那么,策略模式是如何解决这些问题的呢?策略模式的核心思想是将不同的算法或行为封装成独立的策略类,这些策略类实现同一个策略接口,使得它们可以相互替换。在上面的支付场景中,我们可以定义一个支付策略接口PaymentStrategy,然后为每种支付方式创建一个具体的策略类来实现这个接口:

// 支付策略接口

public interface PaymentStrategy {

void pay(double amount);

}

// 信用卡支付策略类

public class CreditCardPaymentStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用信用卡支付:" + amount);

}

}

// 支付宝支付策略类

public class AlipayPaymentStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用支付宝支付:" + amount);

}

}

// 微信支付策略类

public class WechatPayPaymentStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用微信支付:" + amount);

}

}

接下来,我们创建一个支付上下文类PaymentContext,它持有一个支付策略的引用,并提供一个方法来执行支付操作:


public class PaymentContext {

private PaymentStrategy paymentStrategy;

public PaymentContext(PaymentStrategy paymentStrategy) {

this.paymentStrategy = paymentStrategy;

}

public void executePayment(double amount) {

paymentStrategy.pay(amount);

}

}

        在客户端代码中,我们可以根据需要动态地选择不同的支付策略:

public class Client {

public static void main(String[] args) {

// 使用信用卡支付

PaymentContext creditCardContext = new PaymentContext(new CreditCardPaymentStrategy());

creditCardContext.executePayment(100.0);

// 使用支付宝支付

PaymentContext alipayContext = new PaymentContext(new AlipayPaymentStrategy());

alipayContext.executePayment(200.0);

// 使用微信支付

PaymentContext wechatPayContext = new PaymentContext(new WechatPayPaymentStrategy());

wechatPayContext.executePayment(300.0);

}

}

        通过策略模式,我们将支付逻辑的实现与使用分离,每个支付策略类只负责自己的支付逻辑,使得代码更加清晰、可维护和可扩展。当需要新增支付方式时,只需创建一个新的策略类实现PaymentStrategy接口,而无需修改现有的代码,这正是策略模式的魅力所在。

一、什么是策略模式

(一)定义与概念

        策略模式(Strategy Pattern)是一种行为型设计模式,其定义为:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。该模式使得算法可以独立于使用它的客户端而变化 。简单来说,策略模式将不同的算法封装成独立的类,这些类实现同一个接口,客户端可以根据不同的场景选择不同的算法类来执行相应的操作。这种模式就像是为程序准备了一套 “算法工具箱”,在需要的时候可以灵活地选择和切换工具,而不影响程序的其他部分。

(二)策略模式的角色

  1. 抽象策略(Strategy)

        抽象策略角色是一个接口或抽象类,它定义了一个或多个方法,这些方法代表了一系列相关的算法或行为。所有的具体策略类都必须实现这个接口或继承这个抽象类,以提供具体的算法实现。在我们前面提到的支付系统中,支付策略接口PaymentStrategy就是抽象策略角色:

public interface PaymentStrategy {

void pay(double amount);

}

        这个接口定义了一个pay方法,用于执行支付操作,具体的支付逻辑由实现该接口的具体策略类来完成。通过定义这样的抽象策略,我们可以将支付行为抽象出来,使得不同的支付方式可以通过实现该接口来提供自己的支付实现,从而实现算法的可替换性。

具体策略(Concrete Strategy)

        具体策略类是实现了抽象策略接口的具体类,每个具体策略类封装了一种具体的算法或行为。在支付系统中,CreditCardPaymentStrategy、AlipayPaymentStrategy和WechatPayPaymentStrategy就是具体策略类:

// 信用卡支付策略类

public class CreditCardPaymentStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用信用卡支付:" + amount);

// 这里可以添加实际的信用卡支付逻辑,如连接支付网关、验证信用卡信息等

}

}

// 支付宝支付策略类

public class AlipayPaymentStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用支付宝支付:" + amount);

// 这里可以添加实际的支付宝支付逻辑,如调用支付宝SDK进行支付操作等

}

}

// 微信支付策略类

public class WechatPayPaymentStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用微信支付:" + amount);

// 这里可以添加实际的微信支付逻辑,如调用微信支付API进行支付等

}

}

        每个具体策略类都实现了PaymentStrategy接口的pay方法,提供了具体的支付实现。这些具体策略类之间相互独立,它们的变化不会影响到其他的策略类和客户端代码,符合单一职责原则和开闭原则。

上下文(Context)

        上下文类也称为环境类,它持有一个抽象策略类的引用,并提供一个方法来执行策略对象的方法。上下文类的作用是将客户端与具体的策略类解耦,客户端通过上下文类来调用具体的策略方法,而不需要直接与具体策略类交互。在支付系统中,PaymentContext就是上下文类:

public class PaymentContext {

private PaymentStrategy paymentStrategy;

public PaymentContext(PaymentStrategy paymentStrategy) {

this.paymentStrategy = paymentStrategy;

}

public void executePayment(double amount) {

paymentStrategy.pay(amount);

}

}

        PaymentContext类持有一个PaymentStrategy类型的引用paymentStrategy,并通过构造函数进行初始化。executePayment方法调用paymentStrategy的pay方法来执行具体的支付操作。这样,客户端只需要创建PaymentContext对象,并传入相应的具体策略对象,就可以执行支付操作,而不需要了解具体策略类的实现细节。例如:

public class Client {

public static void main(String[] args) {

// 使用信用卡支付

PaymentContext creditCardContext = new PaymentContext(new CreditCardPaymentStrategy());

creditCardContext.executePayment(100.0);

// 使用支付宝支付

PaymentContext alipayContext = new PaymentContext(new AlipayPaymentStrategy());

alipayContext.executePayment(200.0);

// 使用微信支付

PaymentContext wechatPayContext = new PaymentContext(new WechatPayPaymentStrategy());

wechatPayContext.executePayment(300.0);

}

}

        在客户端代码中,我们通过创建PaymentContext对象,并传入不同的具体策略对象,实现了不同支付方式的调用,而客户端代码与具体的支付策略实现完全解耦,提高了代码的灵活性和可维护性。

二、策略模式的应用场景

(一)电商平台的支付方式选择

        在电商平台中,支付功能是核心模块之一,用户在完成购物后需要进行支付操作,而平台通常会提供多种支付方式供用户选择,如信用卡支付、支付宝支付、微信支付、银联支付等。不同的支付方式有着不同的支付流程和业务逻辑,这正是策略模式的典型应用场景。

        以我们日常使用的淘宝、京东等电商平台为例,当我们在购物车中确认商品并点击结算后,会进入支付页面。在这个页面上,我们可以看到各种支付选项,当我们选择支付宝支付时,系统会调用支付宝支付的策略类,该策略类中封装了与支付宝支付相关的逻辑,如跳转到支付宝的支付页面、传递订单信息、处理支付结果回调等;如果我们选择微信支付,系统则会切换到微信支付的策略类,执行微信支付的一系列操作,包括拉起微信支付界面、验证用户身份、完成支付交易等。

        从代码实现角度来看,在电商平台的支付模块中,首先会定义一个支付策略接口PaymentStrategy,它包含一个支付方法pay:

public interface PaymentStrategy {

void pay(double amount);

}

        然后,为每种支付方式创建一个具体的策略类来实现这个接口。例如,支付宝支付策略类AlipayPaymentStrategy:

public class AlipayPaymentStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

// 调用支付宝支付接口的逻辑,如创建支付订单、发起支付请求等

System.out.println("使用支付宝支付:" + amount);

}

}

        微信支付策略类WechatPayPaymentStrategy:

public class WechatPayPaymentStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

// 调用微信支付接口的逻辑,如调用微信SDK、处理支付流程等

System.out.println("使用微信支付:" + amount);

}

}

        在支付上下文类PaymentContext中,持有一个支付策略的引用,并提供执行支付的方法:

public class PaymentContext {

private PaymentStrategy paymentStrategy;

public PaymentContext(PaymentStrategy paymentStrategy) {

this.paymentStrategy = paymentStrategy;

}

public void executePayment(double amount) {

paymentStrategy.pay(amount);

}

}

        在用户进行支付操作时,根据用户选择的支付方式创建相应的策略对象,并将其传递给PaymentContext。例如:

// 用户选择支付宝支付

PaymentStrategy alipayStrategy = new AlipayPaymentStrategy();

PaymentContext alipayContext = new PaymentContext(alipayStrategy);

alipayContext.executePayment(100.0);

// 用户选择微信支付

PaymentStrategy wechatStrategy = new WechatPayPaymentStrategy();

PaymentContext wechatContext = new PaymentContext(wechatStrategy);

wechatContext.executePayment(200.0);

        通过这种方式,电商平台的支付模块实现了支付方式的灵活切换和扩展。当需要新增一种支付方式时,只需要创建一个新的策略类实现PaymentStrategy接口,而不需要修改现有的支付逻辑代码,大大提高了系统的可维护性和可扩展性。同时,每个支付策略类只负责自己的支付逻辑,遵循了单一职责原则,使得代码结构更加清晰。

(二)游戏开发中的角色行为

        在游戏开发中,策略模式被广泛应用于实现角色的多样化行为。以常见的角色扮演游戏(RPG)为例,游戏中的角色通常具有多种行为,如攻击、防御、移动、释放技能等,而且不同类型的角色(如战士、法师、刺客等)在执行这些行为时会有不同的表现和效果。

        以攻击行为来说,战士角色可能会使用近战武器进行物理攻击,攻击范围较短但伤害较高;法师角色则通过释放法术进行远程魔法攻击,攻击范围广但可能需要一定的施法时间;刺客角色擅长进行突袭和暴击攻击,追求一击必杀的效果。这些不同的攻击行为就可以通过策略模式来实现。

        我们可以定义一个攻击策略接口AttackStrategy,其中包含一个攻击方法attack:

public interface AttackStrategy {

void attack();

}

        然后,为不同角色的攻击行为创建具体的策略类。比如战士的攻击策略类WarriorAttackStrategy:

public class WarriorAttackStrategy implements AttackStrategy {

@Override

public void attack() {

System.out.println("战士挥舞大剑,进行强力的近战攻击!");

// 这里可以添加实际的伤害计算、攻击特效等逻辑

}

}

        法师的攻击策略类MageAttackStrategy:

public class MageAttackStrategy implements AttackStrategy {

@Override

public void attack() {

System.out.println("法师吟唱咒语,释放强大的魔法攻击!");

// 可以添加魔法伤害计算、魔法特效等逻辑

}

}

        刺客的攻击策略类AssassinAttackStrategy:

public class AssassinAttackStrategy implements AttackStrategy {

@Override

public void attack() {

System.out.println("刺客瞬间突袭,给予敌人致命一击!");

// 可以添加暴击几率计算、偷袭效果等逻辑

}

}

        在角色类Character中,持有一个攻击策略的引用,并提供一个执行攻击的方法:

public class Character {

private String name;

private AttackStrategy attackStrategy;

public Character(String name, AttackStrategy attackStrategy) {

this.name = name;

this.attackStrategy = attackStrategy;

}

public void performAttack() {

System.out.println(name + "发动攻击:");

attackStrategy.attack();

}

}

        在游戏中创建不同角色时,就可以为其设置相应的攻击策略。例如:

// 创建战士角色

AttackStrategy warriorAttack = new WarriorAttackStrategy();

Character warrior = new Character("战士张三", warriorAttack);

warrior.performAttack();

// 创建法师角色

AttackStrategy mageAttack = new MageAttackStrategy();

Character mage = new Character("法师李四", mageAttack);

mage.performAttack();

// 创建刺客角色

AttackStrategy assassinAttack = new AssassinAttackStrategy();

Character assassin = new Character("刺客王五", assassinAttack);

assassin.performAttack();

        通过策略模式,游戏开发者可以轻松地为不同角色定义各种行为策略,并且在游戏运行过程中根据需要动态地切换角色的行为。比如在某些特殊剧情或任务中,角色可能会获得特殊的技能或能力,这时只需要切换其行为策略即可实现,而不需要大量修改角色类的代码,提高了游戏开发的灵活性和可维护性,同时也为玩家带来更加丰富多样的游戏体验。

(三)数据处理中的算法选择

        在数据处理领域,经常会遇到需要根据不同的数据特点和业务需求选择不同算法的情况。例如,在对数据进行排序时,对于小规模数据,简单的冒泡排序或插入排序可能就足够了,它们实现简单且在小规模数据上性能表现尚可;而对于大规模数据,快速排序、归并排序等高效算法则更合适,能够大大提高排序效率。又比如在数据加密场景中,根据数据的敏感程度和安全要求,可能会选择不同强度的加密算法,如对于一般的用户数据,使用 AES 加密算法即可满足安全性需求;对于高度敏感的金融数据,则可能需要采用更高级的 RSA 加密算法。

        以排序算法为例,我们可以定义一个排序策略接口SortingStrategy,其中包含一个排序方法sort:

public interface SortingStrategy {

void sort(int[] array);

}

        然后实现不同的排序策略类,如冒泡排序策略类BubbleSortStrategy:

public class BubbleSortStrategy implements SortingStrategy {

@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]) {

int temp = array[j];

array[j] = array[j + 1];

array[j + 1] = temp;

}

}

}

System.out.println("使用冒泡排序完成排序");

}

}

        快速排序策略类QuickSortStrategy:

public class QuickSortStrategy implements SortingStrategy {

@Override

public void sort(int[] array) {

quickSort(array, 0, array.length - 1);

System.out.println("使用快速排序完成排序");

}

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;

}

}

        创建一个数据处理上下文类DataProcessor,持有排序策略的引用,并提供执行排序的方法:

public class DataProcessor {

private SortingStrategy sortingStrategy;

public DataProcessor(SortingStrategy sortingStrategy) {

this.sortingStrategy = sortingStrategy;

}

public void processData(int[] data) {

sortingStrategy.sort(data);

// 可以在此处添加排序后的数据处理逻辑,如输出排序后的数据

for (int num : data) {

System.out.print(num + " ");

}

System.out.println();

}

}

        在实际的数据处理过程中,根据数据规模或其他条件选择合适的排序策略。例如:

// 处理小规模数据,选择冒泡排序

SortingStrategy bubbleSort = new BubbleSortStrategy();

DataProcessor smallDataProcessor = new DataProcessor(bubbleSort);

int[] smallData = {5, 3, 4, 6, 2};

smallDataProcessor.processData(smallData);

// 处理大规模数据,选择快速排序

SortingStrategy quickSort = new QuickSortStrategy();

DataProcessor largeDataProcessor = new DataProcessor(quickSort);

int[] largeData = {12, 35, 1, 19, 4, 36, 2};

largeDataProcessor.processData(largeData);

        通过策略模式,数据处理系统可以根据不同的需求灵活地选择和切换算法,提高了数据处理的效率和适应性。同时,将不同算法封装成独立的策略类,使得代码结构更加清晰,易于维护和扩展。当有新的算法出现或现有算法需要改进时,只需要创建新的策略类或修改相应策略类的实现,而不会影响到整个数据处理系统的其他部分。

三、策略模式的代码实现

(一)以支付系统为例

定义抽象策略接口

        在支付系统中,首先需要定义一个抽象的支付策略接口,它定义了支付的基本行为。以 Java 代码为例:

// 支付策略接口

public interface PaymentStrategy {

void pay(double amount);

}

        这个接口非常简洁,只包含一个pay方法,用于执行支付操作,参数amount表示支付的金额。所有具体的支付策略类都必须实现这个接口,从而保证了支付行为的一致性和可扩展性。

实现具体策略类

        接下来,我们创建具体的支付策略类,分别实现微信支付和支付宝支付的逻辑。

  • 微信支付策略类WeChatPayStrategy:
public class WeChatPayStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用微信支付:" + amount);

// 这里可以添加实际调用微信支付接口的代码,如构建支付请求参数、调用微信支付SDK等

// 例如:WXPay wxPay = new WXPay(wxConfig); // 初始化微信支付对象

// Map<String, String> data = new HashMap<>(); // 构建支付请求参数

// data.put("body", "商品描述");

// data.put("out_trade_no", "订单号");

// data.put("total_fee", amount * 100 + ""); // 金额单位为分

// wxPay.unifiedOrder(data); // 发起微信支付统一下单请求

}

}

        在这个类中,pay方法实现了微信支付的逻辑,首先打印出支付信息,实际应用中还会涉及到与微信支付接口的交互,如获取用户的支付授权、传递订单信息到微信支付服务器等。

  • 支付宝支付策略类AlipayStrategy:
public class AlipayStrategy implements PaymentStrategy {

@Override

public void pay(double amount) {

System.out.println("使用支付宝支付:" + amount);

// 这里可以添加实际调用支付宝支付接口的代码,如使用支付宝开放平台的SDK进行支付操作

// 例如:AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", appId, privateKey, "json", charset, alipayPublicKey, "RSA2");

// AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();

// alipayRequest.setReturnUrl(returnUrl);

// alipayRequest.setNotifyUrl(notifyUrl);

// alipayRequest.setBizContent("{\"out_trade_no\":\"" + outTradeNo + "\"," + "\"total_amount\":\"" + amount + "\"," + "\"subject\":\"" + subject + "\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

// alipayClient.pageExecute(alipayRequest); // 执行支付宝支付请求

}

}

        同样,AlipayStrategy类的pay方法实现了支付宝支付的逻辑,除了打印支付信息外,还会包含与支付宝支付平台进行交互的实际代码,如创建支付宝支付订单、处理支付结果回调等。

创建上下文类

        上下文类PaymentContext持有一个支付策略的引用,并提供一个方法来执行支付操作,它将客户端与具体的支付策略解耦。

public class PaymentContext {

private PaymentStrategy paymentStrategy;

public PaymentContext(PaymentStrategy paymentStrategy) {

this.paymentStrategy = paymentStrategy;

}

public void executePayment(double amount) {

paymentStrategy.pay(amount);

}

}

        在PaymentContext类中,通过构造函数传入一个PaymentStrategy对象,然后在executePayment方法中调用该策略对象的pay方法来执行实际的支付操作。这样,客户端只需要与PaymentContext类交互,而不需要关心具体的支付策略实现细节。

客户端调用

        在客户端代码中,我们可以根据用户的选择动态地创建不同的支付策略对象,并使用PaymentContext来执行支付操作。

public class Client {

public static void main(String[] args) {

// 使用微信支付

PaymentStrategy weChatPayStrategy = new WeChatPayStrategy();

PaymentContext weChatPayContext = new PaymentContext(weChatPayStrategy);

weChatPayContext.executePayment(100.0);

// 使用支付宝支付

PaymentStrategy alipayStrategy = new AlipayStrategy();

PaymentContext alipayContext = new PaymentContext(alipayStrategy);

alipayContext.executePayment(200.0);

}

}

        在Client类的main方法中,首先创建了一个WeChatPayStrategy对象,并将其传递给PaymentContext的构造函数,创建weChatPayContext对象,然后调用executePayment方法执行微信支付操作,支付金额为 100.0。接着,创建AlipayStrategy对象并重复上述步骤,执行支付宝支付操作,支付金额为 200.0。通过这种方式,客户端可以灵活地选择不同的支付方式,而不需要修改大量的代码。

(二)代码分析与优化

代码结构分析

        从上述代码可以看出,策略模式将支付逻辑进行了清晰的分离。PaymentStrategy接口定义了支付的抽象行为,WeChatPayStrategy和AlipayStrategy类分别实现了具体的支付策略,它们之间相互独立,遵循单一职责原则。PaymentContext类作为上下文,将支付策略的使用和实现解耦,使得客户端代码更加简洁和灵活。当需要新增支付方式时,只需要创建一个新的具体策略类实现PaymentStrategy接口,而不需要修改PaymentContext和客户端代码,符合开闭原则。这种结构使得代码的维护和扩展变得非常容易,提高了代码的可维护性和可扩展性。

策略模式在代码维护和扩展方面的优势

  • 可维护性高:每个具体策略类只负责自己的支付逻辑,当某个支付方式的逻辑发生变化时,只需要修改对应的策略类,不会影响到其他支付方式的代码。例如,如果微信支付的接口发生了变化,只需要在WeChatPayStrategy类中修改相关代码,而支付宝支付的代码不受影响。

  • 扩展性强:新增支付方式变得非常简单,只需要创建一个新的策略类实现PaymentStrategy接口,然后在客户端代码中使用该策略类即可。比如,要新增银联支付方式,只需要创建UnionPayStrategy类实现PaymentStrategy接口,然后在客户端创建UnionPayStrategy对象并传递给PaymentContext,就可以实现银联支付功能,不需要对现有代码进行大规模修改。

  • 代码复用性好:PaymentStrategy接口和PaymentContext类可以被多种支付方式复用,提高了代码的复用率。不同的支付策略类可以共享相同的上下文和接口,减少了重复代码的编写。

优化建议:使用工厂模式创建策略对象

        虽然策略模式已经使代码具有良好的结构和扩展性,但在客户端创建策略对象时,仍然需要直接实例化具体的策略类,这在一定程度上增加了客户端与具体策略类的耦合度。为了进一步降低耦合度,可以引入工厂模式来创建策略对象。

  • 创建支付策略工厂类PaymentStrategyFactory
public class PaymentStrategyFactory {

public static PaymentStrategy getPaymentStrategy(String paymentType) {

if ("wechat".equals(paymentType)) {

return new WeChatPayStrategy();

} else if ("alipay".equals(paymentType)) {

return new AlipayStrategy();

}

throw new IllegalArgumentException("不支持的支付方式: " + paymentType);

}

}

        在这个工厂类中,getPaymentStrategy方法根据传入的支付类型paymentType返回相应的支付策略对象。如果支付类型为"wechat",则返回WeChatPayStrategy对象;如果为"alipay",则返回AlipayStrategy对象;如果不支持的支付类型,则抛出IllegalArgumentException异常。

  • 修改客户端代码
public class Client {

public static void main(String[] args) {

// 使用微信支付

PaymentStrategy weChatPayStrategy = PaymentStrategyFactory.getPaymentStrategy("wechat");

PaymentContext weChatPayContext = new PaymentContext(weChatPayStrategy);

weChatPayContext.executePayment(100.0);

// 使用支付宝支付

PaymentStrategy alipayStrategy = PaymentStrategyFactory.getPaymentStrategy("alipay");

PaymentContext alipayContext = new PaymentContext(alipayStrategy);

alipayContext.executePayment(200.0);

}

}

        修改后的客户端代码通过调用PaymentStrategyFactory的getPaymentStrategy方法来获取支付策略对象,而不需要直接实例化具体的策略类。这样,客户端与具体策略类的耦合度进一步降低,当新增支付方式时,只需要在PaymentStrategyFactory类中添加新的判断逻辑,而客户端代码不需要修改,提高了代码的灵活性和可维护性。同时,工厂模式还可以对策略对象的创建进行统一管理,例如可以在创建策略对象时进行一些初始化操作或缓存策略对象,提高系统的性能和资源利用率。

四、策略模式的优缺点

(一)优点

        开闭原则:策略模式对扩展开放,对修改关闭,符合开闭原则。当需要新增一种策略时,只需创建一个新的具体策略类实现抽象策略接口,而不需要修改现有的上下文类和其他策略类的代码。例如,在电商支付系统中,如果要新增一种新的支付方式,如 Apple Pay 支付,只需要创建ApplePayPaymentStrategy类实现PaymentStrategy接口,然后在客户端代码中使用该策略类即可,不会对已有的微信支付、支付宝支付等策略类和支付上下文类造成影响,极大地提高了系统的可扩展性和维护性 。

        避免多重条件判断:在不使用策略模式时,代码中可能会出现大量复杂的if - else或switch语句来判断不同的情况并执行相应的逻辑。例如,在一个订单折扣计算系统中,如果不使用策略模式,可能会出现如下代码:

public class OrderService {

public double calculateDiscount(Order order) {

double discount = 0.0;

if (order.getType() == OrderType.NORMAL) {

// 普通订单折扣计算逻辑

discount = order.getAmount() * 0.05;

} else if (order.getType() == OrderType.GROUPON) {

// 团购订单折扣计算逻辑

discount = order.getAmount() * 0.1;

} else if (order.getType() == OrderType.PROMOTION) {

// 促销订单折扣计算逻辑

discount = order.getAmount() * 0.15;

}

return discount;

}

}

        这样的代码不仅冗长、可读性差,而且维护起来非常困难。当新增一种订单类型时,需要在这个if - else语句块中添加新的条件分支,容易引入错误。而使用策略模式后,可以将不同订单类型的折扣计算逻辑封装成独立的策略类,如NormalOrderDiscountStrategy、GrouponOrderDiscountStrategy、PromotionOrderDiscountStrategy,然后通过上下文类来调用相应的策略,避免了复杂的条件判断语句,使代码更加简洁、清晰,易于维护和扩展 。

        提高算法可复用性:每个具体策略类都封装了独立的算法或行为,它们可以在不同的场景中独立复用。以排序算法为例,冒泡排序策略类BubbleSortStrategy和快速排序策略类QuickSortStrategy都实现了SortingStrategy接口,它们的排序算法可以在不同的数据处理场景中被复用。比如在一个数据统计系统中,需要对不同类型的数据进行排序,就可以复用这些已有的排序策略类,而不需要重新编写排序算法,提高了代码的复用率,减少了代码的重复编写,提高了开发效率 。

(二)缺点

        类数量增加:策略模式会导致类的数量增多,因为每一个具体策略都需要一个单独的类来实现。在电商支付系统中,如果支持信用卡支付、支付宝支付、微信支付、银联支付、Apple Pay 支付等多种支付方式,就需要创建CreditCardPaymentStrategy、AlipayPaymentStrategy、WechatPayPaymentStrategy、UnionPayPaymentStrategy、ApplePayPaymentStrategy等多个具体策略类。随着业务的发展,支付方式可能会不断增加,类的数量也会随之增多,这会增加项目的维护成本和理解难度,同时也会占用更多的系统资源 。

        客户端需了解策略:客户端必须了解所有的策略类,才能根据不同的需求选择合适的策略。在使用策略模式实现的游戏角色行为系统中,客户端代码需要知道战士的攻击策略类WarriorAttackStrategy、法师的攻击策略类MageAttackStrategy、刺客的攻击策略类AssassinAttackStrategy等,然后根据创建的角色类型选择相应的攻击策略。如果客户端不了解这些策略类,就无法正确地选择合适的策略,这增加了客户端代码的复杂性和使用难度。而且,当策略类的数量较多或者策略类的功能相似难以区分时,客户端选择正确策略的难度会进一步加大 。

五、策略模式与其他设计模式的关系

(一)与工厂模式结合

        在实际应用中,策略模式常常与工厂模式结合使用,以进一步提高代码的灵活性和可维护性。工厂模式主要负责对象的创建,而策略模式专注于算法的封装和切换。通过将两者结合,可以将策略对象的创建过程封装在工厂类中,使得客户端无需直接实例化具体的策略类,从而降低了客户端与具体策略类之间的耦合度。

        以电商支付系统为例,我们之前已经实现了策略模式的支付功能。现在引入工厂模式来创建支付策略对象。首先,创建一个支付策略工厂类PaymentStrategyFactory:

public class PaymentStrategyFactory {

public static PaymentStrategy getPaymentStrategy(String paymentType) {

if ("wechat".equals(paymentType)) {

return new WeChatPayStrategy();

} else if ("alipay".equals(paymentType)) {

return new AlipayStrategy();

}

throw new IllegalArgumentException("不支持的支付方式: " + paymentType);

}

}

        在这个工厂类中,getPaymentStrategy方法根据传入的支付类型paymentType返回相应的支付策略对象。如果支付类型为"wechat",则返回WeChatPayStrategy对象;如果为"alipay",则返回AlipayStrategy对象;如果是不支持的支付类型,则抛出IllegalArgumentException异常。

        然后,修改客户端代码,通过工厂类来获取支付策略对象:

public class Client {

public static void main(String[] args) {

// 使用微信支付

PaymentStrategy weChatPayStrategy = PaymentStrategyFactory.getPaymentStrategy("wechat");

PaymentContext weChatPayContext = new PaymentContext(weChatPayStrategy);

weChatPayContext.executePayment(100.0);

// 使用支付宝支付

PaymentStrategy alipayStrategy = PaymentStrategyFactory.getPaymentStrategy("alipay");

PaymentContext alipayContext = new PaymentContext(alipayStrategy);

alipayContext.executePayment(200.0);

}

}

        通过结合工厂模式,客户端代码变得更加简洁,只需要通过PaymentStrategyFactory获取支付策略对象,而不需要关心具体策略类的创建细节。当需要新增支付方式时,只需要在PaymentStrategyFactory类中添加新的创建逻辑,客户端代码无需修改,进一步提高了代码的可维护性和扩展性。同时,工厂模式还可以对策略对象的创建进行统一管理,例如可以在创建策略对象时进行一些初始化操作或缓存策略对象,提高系统的性能和资源利用率 。

(二)与状态模式的区别

        策略模式和状态模式都是行为型设计模式,它们在结构上有一些相似之处,都涉及到多个类实现同一个接口或继承同一个抽象类,但它们在应用场景和实现方式上存在明显的区别。

应用场景

  • 策略模式:主要用于解决在不同情况下需要选择不同算法或行为的问题。它的重点在于提供一系列可以相互替换的算法,客户端可以根据不同的业务需求动态地选择合适的算法。例如在电商支付系统中,根据用户的偏好选择微信支付、支付宝支付等不同的支付算法;在数据处理中,根据数据规模选择冒泡排序、快速排序等不同的排序算法。策略模式强调的是算法的多样性和可替换性,这些算法之间是平等的关系,客户端可以根据自身需求自由选择 。

  • 状态模式:主要用于解决对象的行为依赖于其内部状态,并且在运行时状态会发生变化从而导致行为改变的问题。它的重点在于根据对象的不同状态来改变其行为。例如在一个游戏角色系统中,角色可能有不同的状态,如正常状态、战斗状态、中毒状态等,在不同状态下角色的行为(如移动速度、攻击力等)会有所不同;在一个订单管理系统中,订单可能有未支付、已支付、已发货、已完成等状态,每个状态下订单的操作(如取消订单、修改订单等)是不同的。状态模式强调的是状态与行为的关联,对象的行为会随着状态的改变而自动改变 。

实现方式

  • 策略模式:客户端需要主动选择并切换策略对象。客户端持有上下文对象,通过构造函数或方法调用将具体的策略对象传递给上下文对象,从而决定上下文对象执行哪种策略。例如在前面的支付系统示例中,客户端根据用户选择创建微信支付策略对象或支付宝支付策略对象,并传递给支付上下文对象PaymentContext,然后调用executePayment方法执行相应的支付策略 。

  • 状态模式:对象的状态转换通常由对象自身内部的逻辑控制,而不是由客户端直接控制。上下文对象持有一个状态对象的引用,当对象的状态发生变化时,上下文对象会自动切换到相应的状态对象,从而改变其行为。例如在订单管理系统中,当订单状态从 “未支付” 变为 “已支付” 时,订单对象会自动切换到 “已支付” 状态对应的状态类,其行为也会相应改变,如不再允许取消订单,而是可以进行发货操作等,这个状态转换过程是在订单对象内部根据业务逻辑完成的,客户端只需要调用订单对象的相关方法,无需关心具体的状态转换细节 。

        总之,策略模式和状态模式虽然有相似之处,但在实际应用中需要根据具体的业务需求和场景来选择使用。如果关注的是算法的切换和选择,应优先考虑策略模式;如果关注的是对象状态变化引起的行为改变,则应优先考虑状态模式。

六、总结与展望

        策略模式作为一种强大的行为型设计模式,为我们在软件开发中应对多样化的业务需求提供了优雅的解决方案。它通过将不同的算法或行为封装成独立的策略类,实现了算法与客户端的解耦,使得系统在面对变化时更加灵活和可维护。

        从电商平台的支付方式选择,到游戏开发中的角色行为设定,再到数据处理中的算法抉择,策略模式在各个领域都有着广泛的应用。它不仅让我们的代码结构更加清晰,遵循了开闭原则、单一职责原则等设计原则,还避免了复杂的多重条件判断,提高了代码的可读性和可扩展性。

        当然,策略模式也并非完美无缺,它可能会导致类的数量增加,并且要求客户端对策略类有一定的了解。但总体而言,其优点远远超过了缺点,尤其是在处理复杂业务逻辑和多变的需求时,策略模式的优势尤为突出。

        在未来的软件开发中,随着业务的不断发展和变化,我们会面临更多需要灵活应对的场景。希望大家能够熟练掌握策略模式,将其巧妙地运用到实际项目中。当你在项目中遇到需要根据不同条件选择不同算法或行为的情况时,不妨想想策略模式,它可能就是你解决问题的钥匙。同时,也鼓励大家进一步探索设计模式的世界,了解更多模式之间的组合和应用,不断提升自己的软件设计能力,创造出更加高质量、可维护的软件系统 。

更多推荐