设计模式:策略模式

本文翻译自Design Patterns: The Strategy Pattern

目前为止我们已经在这个系列中接触了三个设计模式。我们定义了4种类型的设计模式。在这篇文章中,我将讲解 策略模式,这是属于行为类别的设计模式的。

你可能会有一个疑问:我们什么时候该使用这个模式呢?当我们有不同的方式(算法)来执行同样的操作,而我们希望应用可以根据传入的参数来选择合适的方式去执行。

一个非常简单的例子就是排序。例如,我们有不同的算法来排序数组元素,但是需要根据数组中元素的个数来选择性能最好的算法。

问题

我将拿一个电子商务网站来作为例子。这个网站有多种支付通道,但是这些支付请求不会在前端显示出来,而是会根据用户购物车里面的商品价值来选择合适的支付通道。

一个实际点的例子就是,如果购物车里的商品价值少于$500,应该选择标准的PayPal支付通道,但是如果大于或等于$500,那么应该使用信用卡支付通道(假设已经收集了用户的信用卡信息)。

如果没有实现一个合适的策略,我们的代码将会像下面这样:

首先,我们有一个主类,包含了使用Paypal和信用卡支付的方法:

// Class to pay using Credit Card
class payByCC {

    private $ccNum = '';
    private $ccType = '';
    private $cvvNum = '';
    private $ccExpMonth = '';
    private $ccExpYear = '';

    public function pay($amount = 0) {
        echo "Paying ". $amount. " using Credit Card";
    }

}

// Class to pay using PayPal
class payByPayPal {

    private $payPalEmail = '';

    public function pay($amount = 0) {
        echo "Paying ". $amount. " using PayPal";
    }

}

// This code needs to be repeated every place where ever needed.
$amount  = 5000;
if($amount >= 500) {
    $pay = new payByCC();
    $pay->pay($amount);
} else {
    $pay = new payByPayPal();
    $pay->pay($amount);
}

想象一下,上面中最后的一段代码将会出现在程序的各个地方,如果有一个新的逻辑需要添加或者需要改变旧的逻辑,你需要在每个出现这段代码的地方修补,这是很容易导致bug的。

解决方法

我们将使用策略模式实现同样的需求,这会让我们的代码非常整洁,易懂,可扩展。

接口

首先,我们定义一个接口,让所有不同的支付类实现这个接口:

interface payStrategy {
    public function pay($amount);
}

class payByCC implements payStrategy {


    private $ccNum = '';
    private $ccType = '';
    private $cvvNum = '';
    private $ccExpMonth = '';
    private $ccExpYear = '';

    public function pay($amount = 0) {
        echo "Paying ". $amount. " using Credit Card";
    }

}

class payByPayPal implements payStrategy {

    private $payPalEmail = '';

    public function pay($amount = 0) {
        echo "Paying ". $amount. " using PayPal";
    }

}

接下来,我们将创建主类,可以使用我们已经创建的不同的策略。

class shoppingCart {

    public $amount = 0;

    public function __construct($amount = 0) {
        $this->amount = $amount;
    }

    public function getAmount() {
        return $this->amount;
    }

    public function setAmount($amount = 0) {
        $this->amount = $amount;
    }

    public function payAmount() {
        if($this->amount >= 500) {
            $payment = new payByCC();
        } else {
            $payment = new payByPayPal();
        }

        $payment->pay($this->amount);

    }
}

这里你可以看到,我们的条件加载不同支付方法放在了payAmount 方法中。让我们把所有代码组合起来,看看我们如何使用这个:

interface payStrategy {
    public function pay($amount);
}

class payByCC implements payStrategy {

    private $ccNum = '';
    private $ccType = '';
    private $cvvNum = '';
    private $ccExpMonth = '';
    private $ccExpYear = '';

    public function pay($amount = 0) {
        echo "Paying ". $amount. " using Credit Card";
    }

}

class payByPayPal implements payStrategy {

    private $payPalEmail = '';

    public function pay($amount = 0) {
        echo "Paying ". $amount. " using PayPal";
    }

}

class shoppingCart {

    public $amount = 0;

    public function __construct($amount = 0) {
        $this->amount = $amount;
    }

    public function getAmount() {
        return $this->amount;
    }

    public function setAmount($amount = 0) {
        $this->amount = $amount;
    }

    public function payAmount() {
        if($this->amount >= 500) {
            $payment = new payByCC();
        } else {
            $payment = new payByPayPal();
        }

        $payment->pay($this->amount);
    }
}

$cart = new shoppingCart(499);
$cart->payAmount();

// Output
Paying 499 using PayPal

$cart = new shoppingCart(501);
$cart->payAmount();

//Output 
Paying 501 using Credit Card

你可以看到,支付通道的选择对于应用来说是不透明的。根据传入的参数,它可以选择可用的,合适的支付通道。

添加一个新的策略

如果过了不久,用户需要添加一个新的策略(新的支付通道),用不同的逻辑,这种情况将会非常简单。假如我们需要添加一个新的支付通道 moneybooker, 当购物车商品价值多于$500,小于$1000时,使用这种通道。

我们只需要创建一个新的策略类,实现我们定义的接口:

class payByMB implements payStrategy {

    private $mbEmail = '';
 
    public function pay($amount = 0) {
        echo "Paying ". $amount. " using Money Booker";
    }

}

有了新的策略类后,我们需要在主方法 payAmount 中进行相应的修改:

public function payAmount() {
 
    if($this->amount > 500 && $this->amount < 1000) {
        $payment = new payByMB();
    } else if($this->amount >= 500) {
        $payment = new payByCC();
    } else {
        $payment = new payByPayPal();
    }
 
    $payment->pay($this->amount);
}

这样就可以了,只需要修改 payAmount 方法。

总结

当我们有不同的方式去执行同样的任务时(在软件编程语言中就是有不同的算法去执行同样的操作),我们就应该考虑使用策略模式。

转载于:https://www.cnblogs.com/YungMing/p/4380379.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值