• 设计模式:策略模式


    本文翻译自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 方法。

    总结

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

  • 相关阅读:
    linux运维、架构之路-MySQL主从复制
    多线程
    IO
    查看信息
    乱码
    字节流与字符流
    file
    JDBC
    规范
    Java常用包
  • 原文地址:https://www.cnblogs.com/YungMing/p/4380379.html
Copyright © 2020-2023  润新知