引言:多种支付方式的业务场景
在日常业务开发中,用户支付场景通常涉及多种支付方式,比如支付宝、微信、钱包等。为了满足用户的需求,系统需要支持这些不同的支付方式。通常情况下,用户发起支付时,我们会根据支付类型执行相应的支付逻辑。
假设我们不使用设计模式,直接写出多种支付逻辑的代码,代码可能如下所示:
<?php
class PaymentService {
public function pay($paymentType, $amount) {
if ($paymentType === 'Alipay') {
// 支付宝支付逻辑
echo "Using Alipay to pay $amount";
} elseif ($paymentType === 'WeChat') {
// 微信支付逻辑
echo "Using WeChat to pay $amount";
} elseif ($paymentType === 'Wallet') {
// 钱包支付逻辑
echo "Using Wallet to pay $amount";
} else {
throw new Exception("Unknown payment type");
}
}
}
// 使用示例
$paymentService = new PaymentService();
$paymentService->pay('Alipay', 100);
在这种实现方式下,我们将所有的支付逻辑写在了 pay()
方法内,根据支付类型进行条件判断,然后执行不同的支付逻辑。
这样写的问题
- 代码难以维护
随着支付方式的增加或变更,pay()
方法内的条件判断也会越来越多,代码变得臃肿、难以维护,每次新增支付方式都需要修改方法内部的逻辑,容易出错。 - 不符合开闭原则
这种写法违反了开闭原则(对扩展开放,对修改封闭)。每当需要添加新的支付方式,就必须修改已有代码,这样的设计增加了代码的修改频率和出错风险。 - 不利于扩展和复用
各种支付方式的逻辑都耦合在一起,代码难以复用。例如,如果需要将支付逻辑单独封装成模块,便于在其他地方调用,代码将需要进行较多的拆分和重构。
为了优化这种业务场景的代码,我们可以采用设计模式来进行重构。在众多设计模式中,策略模式非常适合这种情况,可以帮助我们将不同的支付方式封装成独立的策略类,使代码结构更加清晰、灵活,易于扩展。
策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,旨在定义一系列算法或行为,并将它们封装到独立的策略类中,以便在运行时动态选择和切换。通过策略模式,客户端代码可以在不同策略之间自由切换,而不需要直接依赖具体的实现类,从而实现代码的灵活性和扩展性。
原理
策略模式的核心思想是将不同的算法、行为或业务逻辑封装在独立的策略类中,通过策略接口将它们统一起来,并通过上下文类在运行时决定选择哪个策略。这样实现了算法的灵活切换,同时减少了代码的冗余和重复。
策略模式一般包括以下几个部分:
- 策略接口:定义了所有策略类必须实现的通用方法,使得具体策略可以通过相同的接口被调用。
- 具体策略类:每个具体策略类实现策略接口,并包含具体的算法或行为。
- 上下文类:在客户端和策略类之间起到连接作用,通过组合的方式接收策略对象并调用策略中的方法。
这种设计可以使得不同策略类之间互不影响,客户端只需要依赖接口,而不依赖具体的实现类。
适用场景
策略模式适用于以下场景:
- 多种算法或行为可替换
当系统中有多个可以替换的算法或行为(如不同的排序算法、不同的计算逻辑等),并且客户端需要根据不同条件选择不同算法时,策略模式能够很好地组织和管理这些算法。 - 需要动态切换算法或行为
当系统需要根据某些条件在运行时动态切换算法或行为时,策略模式允许在不修改客户端代码的前提下,灵活选择或替换不同的算法。 - 消除多重条件判断
在代码中,如果某个方法包含大量的条件判断(如if-else
或switch-case
),可以将不同的条件逻辑封装到不同的策略类中,以简化代码逻辑,减少条件判断。
实际业务场景
策略模式在实际业务中应用广泛,尤其在以下场景中效果显著:
-
支付方式选择
在支付系统中,用户可以选择不同的支付方式(如支付宝、微信、信用卡等)。每种支付方式的实现逻辑不同,策略模式可以将每种支付方式的逻辑独立封装,客户端可以根据用户的选择在运行时动态应用不同的支付策略。 -
促销活动和优惠策略
在电商促销活动中,不同的活动可能有不同的优惠计算方式(如满减、打折、满赠等)。通过策略模式,可以将不同的促销方式封装成独立的策略类,动态选择合适的优惠策略,以便灵活适应业务需求的变化。 -
物流配送策略
不同的物流公司和配送方式有不同的计费标准和配送时效。策略模式可以封装不同的物流配送逻辑,在运行时根据用户的选择或系统的配置,动态切换配送策略。 -
内容推荐系统
在推荐系统中,可以使用不同的推荐算法(如热门推荐、个性化推荐、基于时间的推荐等)。策略模式可以封装不同的推荐算法,使得系统可以灵活切换推荐策略,向用户展示最合适的内容。 -
数据排序与筛选
在数据处理系统中,不同的业务场景可能需要不同的排序或筛选规则。策略模式可以封装这些规则,使得代码更清晰,并且能够在运行时根据条件自由切换不同的排序或筛选方式。 -
动态业务规则
某些业务系统中会有一系列动态变化的规则(如保险定价规则、税率计算等),策略模式可以帮助将这些规则逻辑封装为策略,使系统能够根据不同条件灵活地应用相应规则,确保业务逻辑的灵活性。
工厂模式和策略模式的业务范围是否相同?
我之前在设计这部分业务的时候 有过一个疑问: 正常我通常会选择使用策略模式来优化这种场景代码结构,使得不同的算法或行为可以灵活替换。但是在这些场景中,是否可以用工厂模式来替代策略模式,达到同样的效果?工厂模式和策略模式的业务范围是否重叠,或者说它们的使用范围相同吗?
为了解这种问题,我们可以先来看看工厂模式与策略模式的的区别:
工厂模式与策略模式的区别
-
关注点
- 工厂模式主要关注对象的创建。它通过工厂类来选择和创建具体的对象实例,从而将对象的创建过程与使用过程解耦。
- 策略模式主要关注行为的替换。策略模式将算法或行为封装到独立的策略类中,通过接口管理,以便在运行时灵活选择或替换。
-
使用场景
- 工厂模式适用于需要根据条件创建不同对象的情况。例如,创建不同支付类型的对象(如支付宝、微信支付等),可以用工厂模式来管理这些对象的创建。
- 策略模式适用于需要动态替换算法或行为的情况。例如,支付中的折扣策略或物流中的计费规则等场景,可以用策略模式在运行时灵活选择具体的策略。
-
扩展性
- 工厂模式的扩展性体现在能通过新增工厂类来创建新的对象实例。
- 策略模式的扩展性体现在通过新增策略类来添加新的行为,使系统能够在不修改客户端代码的前提下,灵活应用新的策略。
-
设计目标
- 工厂模式的设计目标是对象的创建解耦,通过工厂来隐藏对象创建的细节。
- 策略模式的设计目标是行为的灵活替换,通过不同的策略类来动态切换具体的算法或行为。
如何选择:工厂模式和策略模式的结合使用
实际上,在许多业务场景中,我们可以将工厂模式和策略模式结合使用。例如,在支付场景中,我们可以:
- 使用工厂模式来创建不同的支付服务实例,如支付宝支付、微信支付等。
- 使用策略模式来封装支付过程中的折扣策略或计费逻辑,使得不同支付方式在执行支付时可以应用不同的算法或规则。
在支付场景中使用策略模式的设计
在支付场景中,我们可以使用策略模式来设计不同支付方式的实现。每种支付方式都实现相同的支付接口,并封装各自的支付逻辑。这样,用户选择不同的支付方式时,系统会调用相应的策略对象来执行支付操作。
1. 定义支付策略接口
首先,定义一个 PaymentStrategy
接口,规定所有支付方式的基本方法 pay
:
<?php
interface PaymentStrategy {
public function pay($amount);
}
2. 创建具体的支付策略类
为每种支付方式创建具体策略类,分别实现 PaymentStrategy
接口,定义不同的支付逻辑:
<?php
class AlipayStrategy implements PaymentStrategy {
public function pay($amount) {
echo "Using Alipay to pay $amount\n";
}
}
class WeChatPayStrategy implements PaymentStrategy {
public function pay($amount) {
echo "Using WeChat to pay $amount\n";
}
}
class WalletPayStrategy implements PaymentStrategy {
public function pay($amount) {
echo "Using Wallet to pay $amount\n";
}
}
3. 创建支付上下文类
上下文类 PaymentContext
持有一个支付策略对象,通过策略对象来执行支付操作:
<?php
class PaymentContext {
private $paymentStrategy;
// 注入具体的支付策略
public function __construct(PaymentStrategy $paymentStrategy) {
$this->paymentStrategy = $paymentStrategy;
}
// 执行支付操作
public function executePayment($amount) {
$this->paymentStrategy->pay($amount);
}
}
4. 使用策略模式
在客户端代码中,根据用户的支付方式选择对应的策略对象,并注入到上下文类中以执行支付操作:
<?php
// 用户选择支付宝支付
$paymentMethod = new AlipayStrategy();
$paymentContext = new PaymentContext($paymentMethod);
$paymentContext->executePayment(100); // 输出: Using Alipay to pay 100
// 用户选择微信支付
$paymentMethod = new WeChatPayStrategy();
$paymentContext = new PaymentContext($paymentMethod);
$paymentContext->executePayment(200); // 输出: Using WeChat to pay 200
这样设计的好处
- 代码的扩展性强
新增支付方式时,只需新增一个具体策略类即可,符合开闭原则,无需修改原有代码。 - 消除多重条件判断
将不同支付方式的逻辑从客户端代码中分离,避免了if-else
或switch-case
的条件判断,使代码结构更加清晰。 - 提高灵活性
支持在运行时动态选择和切换支付方式,客户端代码可以根据业务需求自由切换支付策略。
工厂模式与策略模式结合使用
在实际业务中,可以将工厂模式与策略模式结合使用。工厂模式负责创建具体的策略实例,策略模式负责定义行为。这样我们可以通过工厂类根据不同条件创建具体的策略对象,并传递到上下文中执行。
1. 创建支付策略工厂
定义一个 PaymentStrategyFactory
工厂类,根据支付类型来创建对应的策略对象:
<?php
class PaymentStrategyFactory {
public static function create($type) {
switch ($type) {
case 'Alipay':
return new AlipayStrategy();
case 'WeChat':
return new WeChatPayStrategy();
case 'Wallet':
return new WalletPayStrategy();
default:
throw new Exception("未知支付类型");
}
}
}
2. 使用工厂和策略模式结合的支付流程
在客户端代码中,根据用户选择的支付类型,通过工厂创建相应的支付策略实例,然后注入到上下文类中执行支付操作:
<?php
// 通过工厂创建策略
$paymentMethod = PaymentStrategyFactory::create('Alipay');
$paymentContext = new PaymentContext($paymentMethod);
$paymentContext->executePayment(100); // 输出: Using Alipay to pay 100
// 使用微信支付
$paymentMethod = PaymentStrategyFactory::create('WeChat');
$paymentContext = new PaymentContext($paymentMethod);
$paymentContext->executePayment(200); // 输出: Using WeChat to pay 200
这样结合使用的好处
- 进一步解耦对象创建与行为执行
工厂模式负责策略对象的创建,策略模式负责行为的定义和执行,两者分工明确,客户端代码只需关心如何调用,进一步降低了耦合度。 - 提升代码的复用性
工厂模式和策略模式相互独立且相互配合,使得策略类可以复用于不同的上下文,工厂类可以复用于不同的对象创建,代码复用性提高。 - 增强系统的灵活性和扩展性
工厂模式提供了扩展新的策略实例的途径,策略模式提供了替换行为的方式。两者结合使系统既可以灵活地增加新支付方式,也可以根据需求自由调整支付逻辑,实现了高扩展性和灵活性。
总结
策略模式通过将不同的算法或行为封装在独立的策略类中,使系统能够根据需要动态选择和切换策略。这种设计带来了以下好处:
- 增强扩展性:增加新的策略时,只需创建新的策略类,不需要修改现有代码,符合开闭原则。
- 减少条件判断:将不同算法或行为从客户端代码中分离,避免了大量的
if-else
或switch-case
语句,使代码更简洁和易读。 - 提高灵活性:策略模式让系统能够在运行时灵活选择和替换行为,满足多变的业务需求。
当工厂模式与策略模式结合使用时,工厂模式负责策略实例的创建,策略模式负责行为的定义和执行,两者相辅相成,进一步提升系统的灵活性和维护性:
- 进一步解耦对象创建与行为执行:工厂模式集中管理策略对象的创建,策略模式负责定义和切换行为,使系统结构更清晰,职责分工更明确。
- 提高代码复用性:工厂类和策略类相互独立且能灵活组合,代码能够复用于不同场景,提升代码的复用性。
- 增强系统扩展性:工厂模式使系统能够轻松接入新的策略类,而策略模式则确保行为的替换简洁流畅。这种组合在复杂业务场景下可以带来高度的可扩展性和灵活性。
通过这种设计,工厂模式和策略模式的结合为系统提供了强大的扩展能力,使代码在应对复杂需求和多变业务时更加简洁、高效、易维护。