简介
场景
某个功能需要从多种算法中根据条件选择一种时,有两个方案:
- 将所有算法硬编码到代码中,然后通过条件语句进行选择
- 使用策略模式使系统可以灵活地选择算法,并可以灵活添加新算法
如果条件是固定的,比如根据星期几来选择算法,则可以通过条件语句硬编码。但是如果条件可能增加,可以使用策略模式。
模式定义
定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。
每个封装不同算法的类称为策略(Strategy)类。
模式特点
策略模式包含三个角色:
- Context:环境类
- Strategy:抽象策略类
- ConcreteStrategy:具体策略类
优缺点
- 策略模式将算法的定义和使用分离,符合开放封闭原则,易于扩展
- 调用者可以自己决定使用哪个算法,灵活性提供的同时,使用难度也增大
PHP 代码示例
对于国际贸易,结算时,每个国家的税费计算方法都不同:
<?php
abstract class TaxStrategy
{
abstract public function calculate(array $arr);
}
class CNTax extends TaxStrategy
{
public function calculate(array $arr)
{
return 0.1 * $arr['fee'];
}
}
class FRTax extends TaxStrategy
{
public function calculate(array $arr)
{
return 0.2 * $arr['fee'];
}
}
class SalesOrder
{
private $taxStrategy;
public function __construct(TaxStrategy $s) {
$this->taxStrategy = $s;
}
public function calcTax() {
$context = array(
'fee' => 100,
);
$val = $this->taxStrategy->calculate($context); // 多态调用
echo $val.PHP_EOL;
}
}
$cn = new CNTax();
$so = new SalesOrder($cn);
$so->calcTax();
$fr = new FRTax();
$so = new SalesOrder($fr);
$so->calcTax();
Golang 代码示例
package main
import "fmt"
// 接口定义策略方法
type TaxStrategy interface {
calculate(data map[string]float32) float32
}
// 策略一
type CNTax struct {}
func (cn *CNTax) calculate(data map[string]float32) float32 {
return 0.1 * data["fee"]
}
// 策略二
type FRTax struct {}
func (fr *FRTax) calculate(data map[string]float32) float32 {
return 0.3 * data["fee"]
}
type SalesOrder struct {
taxStrategy TaxStrategy
}
func NewSalesOrder() *SalesOrder {
return &SalesOrder{}
}
func (so *SalesOrder) setTaxStrategy(taxStrategy TaxStrategy) {
so.taxStrategy = taxStrategy
}
func (so *SalesOrder) CalcTax(data map[string]float32) float32 {
return so.taxStrategy.calculate(data)
}
func main() {
data := map[string]float32{"fee":100}
so := NewSalesOrder()
// 根据需要实例化不同策略,并注入使用策略的类实例
var cn TaxStrategy = &CNTax{}
so.setTaxStrategy(cn)
fmt.Println("CN fee is: ", so.CalcTax(data))
var fr TaxStrategy = &FRTax{}
so.setTaxStrategy(fr)
fmt.Println("FR fee is: ", so.CalcTax(data))
}