大话设计模式(php版)第二章——策略模式

本文通过收银系统的演变,介绍了如何使用策略模式以提高代码灵活性,避免重复代码。从最初的简单实现到引入工厂模式,再到策略模式的应用,最后讨论了策略模式与简单工厂模式的结合,降低了客户端的耦合度。文章深入浅出地展示了策略模式在处理不同折扣策略时的优势。
摘要由CSDN通过智能技术生成

前言

写一个商场收银相关接口,要求输入当前商品单价和数量,最后展示商品总价及所有商品信息

收银系统1.0

<?php

//收银系统
class store
{
    //总价
    public $total = 0;
    public $goodsList = [];

    //录入价钱
    public function click($goodsName, $price, $num)
    {
        $currentPrice = round($price * $num, 2);
        $this->total = round($this->total + $currentPrice, 2);
        $this->goodsList[] = [
            'goods_name' => $goodsName,
            'price' => $price,
            'num' => $num,
            'all_price' => $currentPrice,
        ];
    }

    //结算金额
    public function show()
    {
        echo "总价:$this->total<br/>";
        foreach ($this->goodsList as $item) {
            echo "{$item['goods_name']} {$item['price']}X{$item['num']}={$item['all_price']}<br/>";
        }
    }
}

$store = new store();
$store->click('苹果', 2, 5.5);
$store->click('香蕉', 5, 2);
$store->click('荔枝', 1, 11);
$store->click('橙子', 2, 1.6);
$store->click('车厘子', 2, 60);
$store->click('榴莲', 2, 42);
$store->show();

输出内容:

总价:239.2
苹果 2X5.5=11
香蕉 5X2=10
荔枝 1X11=11
橙子 2X1.6=3.2
车厘子 2X60=120
榴莲 2X42=84

大鸟指导

  • 现在要求商场搞活动,所有商品打八折,你要怎么改动? 如果是打五折,打七折呢?

收银系统1.1

<?php

//收银系统
class store
{
    //总价
    public $total = 0;
    public $goodsList = [];

    //录入价钱
    public function click($goodsName, $price, $num, $discount = 1)
    {
        switch ($discount) {
            case  1:
                $currentPrice = round($price * $num, 2);
                break;
            case  0.7:
                $currentPrice = round($price * $num * 0.7, 2);
                break;
            case  0.8:
                $currentPrice = round($price * $num * 0.8, 2);
                break;
            case  0.5:
                $currentPrice = round($price * $num * 0.5, 2);
                break;
        }

        $this->total = round($this->total + $currentPrice, 2);
        $this->goodsList[] = [
            'goods_name' => $goodsName,
            'price' => $price,
            'num' => $num,
            'discount' => $discount,
            'all_price' => $currentPrice,
        ];
    }

    //结算金额
    public function show()
    {
        echo "总价:$this->total<br/>";
        foreach ($this->goodsList as $item) {
            if ($item['discount'] == 1) {
                echo "{$item['goods_name']} {$item['price']}X{$item['num']}={$item['all_price']}<br/>";
            } else {
                echo "{$item['goods_name']} {$item['price']}X{$item['num']}X{$item['discount']}={$item['all_price']}<br/>";
            }
        }
    }
}

$store = new store();
$store->click('苹果', 2, 5.5, 0.5);
$store->click('香蕉', 5, 2, 0.5);
$store->click('荔枝', 1, 11, 0.5);
$store->click('橙子', 2, 1.6, 0.5);
$store->click('车厘子', 2, 60, 0.5);
$store->click('榴莲', 2, 42, 0.5);
$store->show();

输出如下

总价:119.6
苹果 2X5.5X0.5=5.5
香蕉 5X2X0.5=5
荔枝 1X11X0.5=5.5
橙子 2X1.6X0.5=1.6
车厘子 2X60X0.5=60
榴莲 2X42X0.5=42

大鸟指导

  1. 现在灵活性好多了,但是重复代码很多
  2. 如果商城出促销活动,满300抵100,你怎么改动这份代码呢
  3. 想想满300抵100,满700抵200,八折,七折,五折这些信息,有哪些相同的,哪些不同的

面向对象的编程,并不是类越多越好,类的划分只是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类

使用工厂模式实现

<?php

//现金收费抽象类
abstract class cashSuper
{
    public abstract function acceptCash($money);
}

//正常收费子类
class cashNormal extends cashSuper
{
    //正常收费,原价返回
    public function acceptCash($money)
    {
        return round($money, 2);
    }
}

//打折收费子类
class cashRebate extends cashSuper
{
    private $moneyRebate;

    //打折费率,初始化时,必须输入打折费率,如八折就是0.8
    public function __construct($moneyRebate)
    {
        $this->moneyRebate = round($moneyRebate, 1);
    }

    //乘以打折费率返回
    public function acceptCash($money)
    {
        return round($money * $this->moneyRebate, 2);
    }
}

//返利收费子类
class cashReturn extends cashSuper
{
    private $moneyCondition; //返现条件
    private $moneyReturn; //返现金额

    //返利收费,初始化时必须输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为100
    public function __construct($moneyCondition, $moneyReturn)
    {
        $this->moneyCondition = round($moneyCondition);
        $this->moneyReturn = round($moneyReturn);
    }

    //如果大于返利条件,则减去返利值
    public function acceptCash($money)
    {

        if ($money >= $this->moneyCondition) {
            return round($money - $this->moneyReturn, 2);
        } else {
            return round($money, 2);
        }
    }
}

//现金收费工厂类
class CashFactory
{
    public function createCashAccept($type)
    {
        $obj = null;
        switch ($type) {
            case '正常收费':
                $obj = new cashNormal();
                break;
            case '满300返100':
                $obj = new cashReturn(300, 100);
                break;
            case '打8折':
                $obj = new cashRebate(0.8);
                break;
        }
        return $obj;
    }
}

//结合之前的场景
class store
{
    //总价
    public $total = 0;
    public $goodsList = [];

    //录入价钱
    public function click($goodsName, $price, $num, $type)
    {
        $totalMoney = round($num * $price, 2);
        $currentPrice = (new CashFactory())->createCashAccept($type)->acceptCash($totalMoney);

        $this->total = round($this->total + $currentPrice, 2);
        $this->goodsList[] = [
            'goods_name' => $goodsName,
            'price' => $price,
            'num' => $num,
            'type' => $type,
            'all_price' => $currentPrice,
        ];
    }

    //结算金额
    public function show()
    {
        echo "总价:$this->total<br/>";
        foreach ($this->goodsList as $item) {
            echo "{$item['goods_name']} {$item['price']}X{$item['num']}[{$item['type']}]=>{$item['all_price']}<br/>";
        }
    }
}

$store = new store();
$store->click('苹果', 2, 5.5, '打8折');
$store->click('香蕉', 5, 2, '正常收费');
$store->click('荔枝', 1, 11, '打8折');
$store->click('橙子', 2, 1.6, '正常收费');
$store->click('车厘子', 6, 60, '满300返100');
$store->click('榴莲', 2, 42, '打8折');
$store->show();

输出如下

总价:358
苹果 2X5.5[打8折]=>8.8
香蕉 5X2[正常收费]=>10
荔枝 1X11[打8折]=>8.8
橙子 2X1.6[正常收费]=>3.2
车厘子 6X60[满300返100]=>260
榴莲 2X42[打8折]=>67.2

大鸟指导

  • 如果要改动优惠力度,就要花心思维护工厂类
  • 虽然工厂类能解决这个问题,但这未必是最好的方法

策略模式样例

<?php

//抽象算法类
abstract  class strategy{
    //算术方法
    public function algorithmInterface(){}
}

class conCreateStrategy extends strategy{
    //算术A实现方法
    public function algorithmInterface()
    {
        echo '算术A实现';
    }
}
class conCreateStrategyB extends strategy{
    //算术B实现方法
    public function algorithmInterface()
    {
        echo '算术B实现';
    }
}
class conCreateStrategyC extends strategy{
    //算术C实现方法
    public function algorithmInterface()
    {
        echo '算术C实现';
    }
}
class context{
    private $strategy;
    public function __construct($strategy)
    {
        $this->strategy = $strategy;
    }
    //上下文接口
    public function contextInterface(){
        $this->strategy->algorithmInterface();
    }
}

//实际调用
(new context(new conCreateStrategy()))->contextInterface();
(new context(new conCreateStrategyB()))->contextInterface();
(new context(new conCreateStrategyC()))->contextInterface();

输出内容:

算术A实现算术B实现算术C实现

大鸟指导

  • 由于实例化不同的策略,最终在调用contextInterface()得到结果就会不一样
    策略模式实现收银系统

策略模式实现收银系统

<?php

//现金收费抽象类
abstract class cashSuper
{
public abstract function acceptCash($money);
}

//正常收费子类
class cashNormal extends cashSuper
{
    //正常收费,原价返回
    public function acceptCash($money)
    {
        return round($money, 2);
    }
}

//打折收费子类
class cashRebate extends cashSuper
{
    private $moneyRebate;

    //打折费率,初始化时,必须输入打折费率,如八折就是0.8
    public function __construct($moneyRebate)
    {
        $this->moneyRebate = round($moneyRebate, 1);
    }

    //乘以打折费率返回
    public function acceptCash($money)
    {
        return round($money * $this->moneyRebate, 2);
    }
}

//返利收费子类
class cashReturn extends cashSuper
{
    private $moneyCondition; //返现条件
    private $moneyReturn; //返现金额

    //返利收费,初始化时必须输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为100
    public function __construct($moneyCondition, $moneyReturn)
    {
        $this->moneyCondition = round($moneyCondition);
        $this->moneyReturn = round($moneyReturn);
    }

    //如果大于返利条件,则减去返利值
    public function acceptCash($money)
    {

        if ($money >= $this->moneyCondition) {
            return round($money - $this->moneyReturn, 2);
        } else {
            return round($money, 2);
        }
    }
}

class cashContext
{
    private $cashObj;

    public function __construct($obj)
    {
        $this->cashObj = $obj;
    }

    public function getResult($money)
    {
        return $this->cashObj->acceptCash($money);
    }
}

//结合之前的场景
class store
{
    //总价
    public $total = 0;
    public $goodsList = [];

    //录入价钱
    public function click($goodsName, $price, $num, $type)
    {
        $totalMoney = round($num * $price, 2);

        $obj = null;
        switch ($type) {
            case '正常收费':
                $obj = new cashContext(new cashNormal());
                break;
            case '满300返100':
                $obj = new cashContext(new cashReturn(300, 100));
                break;
            case '打8折':
                $obj = new cashContext(new cashRebate(0.8));
                break;
        }

        $currentPrice = $obj->getResult($totalMoney);

        $this->total = round($this->total + $currentPrice, 2);
        $this->goodsList[] = [
            'goods_name' => $goodsName,
            'price' => $price,
            'num' => $num,
            'type' => $type,
            'all_price' => $currentPrice,
        ];
    }

    //结算金额
    public function show()
    {
        echo "总价:$this->total<br/>";
        foreach ($this->goodsList as $item) {
            echo "{$item['goods_name']} {$item['price']}X{$item['num']}[{$item['type']}]=>{$item['all_price']}<br/>";
        }
    }
}

$store = new store();
$store->click('苹果', 2, 5.5, '打8折');
$store->click('香蕉', 5, 2, '正常收费');
$store->click('荔枝', 1, 11, '打8折');
$store->click('橙子', 2, 1.6, '正常收费');
$store->click('车厘子', 6, 60, '满300返100');
$store->click('榴莲', 2, 42, '打8折');
$store->show();

输出如下

总价:358
苹果 2X5.5[打8折]=>8.8
香蕉 5X2[正常收费]=>10
荔枝 1X11[打8折]=>8.8
橙子 2X1.6[正常收费]=>3.2
车厘子 6X60[满300返100]=>260
榴莲 2X42[打8折]=>67.2  

大鸟指导

  • 策略模式是出来了,但是判断逻辑又回到了click()方法里
  • 可以让策略模式的Context与简单工厂结合

策略模式与简单工厂结合

<?php

//现金收费抽象类
abstract class cashSuper
{
public abstract function acceptCash($money);
}

//正常收费子类
class cashNormal extends cashSuper
{
    //正常收费,原价返回
    public function acceptCash($money)
    {
        return round($money, 2);
    }
}

//打折收费子类
class cashRebate extends cashSuper
{
    private $moneyRebate;

    //打折费率,初始化时,必须输入打折费率,如八折就是0.8
    public function __construct($moneyRebate)
    {
        $this->moneyRebate = round($moneyRebate, 1);
    }

    //乘以打折费率返回
    public function acceptCash($money)
    {
        return round($money * $this->moneyRebate, 2);
    }
}

//返利收费子类
class cashReturn extends cashSuper
{
    private $moneyCondition; //返现条件
    private $moneyReturn; //返现金额

    //返利收费,初始化时必须输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为100
    public function __construct($moneyCondition, $moneyReturn)
    {
        $this->moneyCondition = round($moneyCondition);
        $this->moneyReturn = round($moneyReturn);
    }

    //如果大于返利条件,则减去返利值
    public function acceptCash($money)
    {

        if ($money >= $this->moneyCondition) {
            return round($money - $this->moneyReturn, 2);
        } else {
            return round($money, 2);
        }
    }
}

class cashContext
{
    private $cashObj;

    //此处传入一个字符串,而不是对象
    public function __construct($objType)
    {
        switch ($objType) {
            case '正常收费':
                $this->cashObj = new cashNormal();
                break;
            case '满300返100':
                $this->cashObj = new cashReturn(300, 100);
                break;
            case '打8折':
                $this->cashObj = new cashRebate(0.8);
                break;
        }
    }

    public function getResult($money)
    {
        return $this->cashObj->acceptCash($money);
    }
}

//结合之前的场景
class store
{
    //总价
    public $total = 0;
    public $goodsList = [];

    //录入价钱
    public function click($goodsName, $price, $num, $type)
    {
        $totalMoney = round($num * $price, 2);

        $currentPrice = (new cashContext($type))->getResult($totalMoney);

        $this->total = round($this->total + $currentPrice, 2);
        $this->goodsList[] = [
            'goods_name' => $goodsName,
            'price' => $price,
            'num' => $num,
            'type' => $type,
            'all_price' => $currentPrice,
        ];
    }

    //结算金额
    public function show()
    {
        echo "总价:$this->total<br/>";
        foreach ($this->goodsList as $item) {
            echo "{$item['goods_name']} {$item['price']}X{$item['num']}[{$item['type']}]=>{$item['all_price']}<br/>";
        }
    }
}

$store = new store();
$store->click('苹果', 2, 5.5, '打8折');
$store->click('香蕉', 5, 2, '正常收费');
$store->click('荔枝', 1, 11, '打8折');
$store->click('橙子', 2, 1.6, '正常收费');
$store->click('车厘子', 6, 60, '满300返100');
$store->click('榴莲', 2, 42, '打8折');
$store->show();

输出如下

总价:358
苹果 2X5.5[打8折]=>8.8
香蕉 5X2[正常收费]=>10
荔枝 1X11[打8折]=>8.8
橙子 2X1.6[正常收费]=>3.2
车厘子 6X60[满300返100]=>260
榴莲 2X42[打8折]=>67.2

大鸟指导

  • 对比简单工厂与策略模式+简单工厂:
    • 简单工厂
      • (new CashFactory())->createCashAccept($type)->acceptCash($totalMoney);
    • 策略模式+简单工厂
      • (new cashContext($type))->getResult($totalMoney)
    • 简单工厂需要认识2个类,CashFactory和cashSuper
    • 策略模式+简单工厂只需要认识一个cashContext,耦合度更低
  • 策略模式是一种定义一系列算法的方式,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同.它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合
  • 策略模式可以封装几乎所有类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑用策略模式处理这种变化的可能性
  • 在基本的策略模式中,选择所用具体实现的职责由客户端对象来承担,并转给策略模式的Context对象,如果结合了简单工厂模式,则可以有Context承担选择判断,减轻客户端的压力
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值