设计模式之美-策略模式

问题场景:

本文中所有代码收录在gitee仓库中,仓库地址: 点我我是仓库地址有需要的小伙伴可以自取

做一个商场收银软件,营业员根据客户所购买商品的单价和数量,向客户收费
在这里插入图片描述

思考:

相信大家一听到这个问题的时候应该已经想到了解决方案了,用两个文本框来输入单价和数量,一个’确定’按钮来算出每种商品的费用,用个列表框来记录商品的清单,一个标签来记录总计,ok话不多少,上代码。
run01类:

public static void main(String[] args) {
        double price = 0d;        //商品单价
        int num = 0;              //商品购买数量
        double totalPrices = 0d;  //当前商品合计费用
        double total = 0d;        //总计所有商品费用
        int discount = 1;         //商品销售模式

        Scanner sc = new Scanner(System.in);

        do {
            System.out.println("请输入商品销售模式 1.原价 2.八折 3.七折");
            discount = Integer.parseInt(sc.nextLine());
            System.out.println("请出入商品单价:");
            price = Double.parseDouble(sc.nextLine());
            System.out.println("请输入商品数量:");
            num = Integer.parseInt(sc.nextLine());
            System.out.println();

            if(price>0 && num>0){
                //通过单价*数量获得当前商品合计费用
                //通过累加获得总价费用
                totalPrices = price * num * discount;
                total = total + totalPrices;

                System.out.println();
                System.out.println("销售模式为:" + discount + "单价:" + price + "元 数量:" + num + "合计:" + totalPrices + "元");
                System.out.println();

                System.out.println("总计:" + total + "元");
                System.out.println();
            }

        }while (price>0 && num>0);
    }

ok,相信大家刚开始想到的结局方案应该都应该差不多,但是现在有一个新问题,这一段代码出现了一些问题

  • 代码不够简洁。
  • 维护和扩展性不高。
  • 如果想新加一个促销模式就不是很方便了。
    此时相信有小伙伴应该会跟我这样写:
    run02类:
public static void main(String[] args) {
        double price = 0d;        //商品单价
        int num = 0;              //商品购买数量
        double totalPrices = 0d;  //当前商品合计费用
        double total = 0d;        //总计所有商品费用
        int discount = 1;         //商品销售模式

        Scanner sc = new Scanner(System.in);

        do {
            System.out.println("请输入商品销售模式 1.原价 2.八折 3.七折");
            discount = Integer.parseInt(sc.nextLine());
            System.out.println("请出入商品单价:");
            price = Double.parseDouble(sc.nextLine());
            System.out.println("请输入商品数量:");
            num = Integer.parseInt(sc.nextLine());
            System.out.println();

            if (price > 0 && num > 0) {
                switch (discount) {
                    case 1:
                        totalPrices = price * num;         //正常收费
                        break;
                    case 2:
                        totalPrices = price * num * 0.8;   //打八折
                        break;
                    case 3:
                        totalPrices = price * num * 0.7;   //打七折
                        break;
                }
                total = total + totalPrices;

                System.out.println();
                System.out.println("销售模式为:" + discount + "单价:" + price + "元 数量:" + num + "合计:" + totalPrices + "元");
                System.out.println();

                System.out.println("总计:" + total + "元");
                System.out.println();
            }

        } while (price > 0 && num > 0);
    }

如果需要别的折扣就再加一个逻辑就行了。

再思考:

这样所说可以解决问题,简单新增折扣倒是还好,如果想要新增稍微复杂的方式就不行,例如满七百减两百,还有积分兑换的促销活动。如果继续在switch块中继续写就不好了。那应该怎么办呢。

  • 我们前面已经学过了简单工厂模式,这个是不是也可以使用简单工厂模式呢。ok开干。

定义收费抽象类

public abstract class CashSuper {

    //定义收费的抽象方法,参数为单价和数量
    public abstract double acceptCash(double price,int num);
}

定义正常收费类

public class CashNormal extends CashSuper{

    @Override
    public double acceptCash(double price, int num) {
        //原价返回
        return price * num;
    }
}

定义打折折扣类

public class CashRebate extends CashSuper{
    //打折收费


    private double moneyRebate = 1d;
    //初始化必须输入折扣率。八折就输入0.8
    public CashRebate(double moneyRebate){
        this.moneyRebate = moneyRebate;
    }


    @Override
    public double acceptCash(double price, int num) {
        //计算收费时需要在原价基础上乘以折扣率
        return price * num + this.moneyRebate;
    }
}

定义积分返回类

public class CashReturn extends CashSuper{

    private double moneyCondition = 0d;   //返利条件
    private double moneyReturn = 0d;      //返利值

    //返利收费。初始化时需要输入返利条件和返利值。
    //比如“满300返100”,就是moneyCondition=300,moneyReturn=100
    public CashReturn(double moneyCondition,double moneyReturn){
        this.moneyCondition = moneyCondition;
        this.moneyReturn = moneyReturn;
    }

    //收费计算时,当达到返利条件,就原价减去返利值
    @Override
    public double acceptCash(double price, int num) {
        double result = price * num;
        if(moneyCondition>0 && result>=moneyCondition){
            result = result - Math.floor(result / moneyCondition) * moneyReturn;
        }
        return result;
    }
}

客户端代码类

public static void main(String[] args) {
        double price = 0d;        //商品单价
        int num = 0;              //商品购买数量
        double totalPrices = 0d;  //当前商品合计费用
        double total = 0d;        //总计所有商品费用
        int discount = 1;         //商品销售模式

        Scanner sc = new Scanner(System.in);

        do {
            System.out.println("请输入商品销售模式 1.原价 2.八折 3.七折");
            discount = Integer.parseInt(sc.nextLine());
            System.out.println("请出入商品单价:");
            price = Double.parseDouble(sc.nextLine());
            System.out.println("请输入商品数量:");
            num = Integer.parseInt(sc.nextLine());
            System.out.println();

            if (price > 0 && num > 0) {

                //简单工厂模式根据discount的数字选择合适的收费类生成实例
                CashSuper csuper = CashFactory.createCashAccept(discount);
                //通过多态,可以根据不同收费策略计算得到收费的结果
                totalPrices = csuper.acceptCash(price,num);
                total = total + totalPrices;

                System.out.println();
                System.out.println("销售模式为:" + discount + "单价:" + price + "元 数量:" + num + "合计:" + totalPrices + "元");
                System.out.println();

                System.out.println("总计:" + total + "元");
                System.out.println();
            }

        } while (price > 0 && num > 0);
    }

这就是利用之前学习的简单工厂模式实现了业务,将各个业务之间分离开,互不影响。这样无论是修改还是新增新的模式都可以直接操作,不会影响到别的类方法。

思考:

如果此时再新增一种促销模式满100积分10点,以后积分到一定时候可以领取奖品,应该实现呢。

  1. 定义新的促销模式类
  2. 新的促销模式类继承收费抽象类
  3. 工厂新增促销模式判断

这样可以实现以上新增的促销模式,但是我们想一下,我们需要修改的类就太多了,商场的促销模式有很多,我们不可能每次有一个新的促销模式就想类似新增,这样不太理想。

所以我们可以使用今日的主题了,策略模式
策略模式(Strategy):它定义了算法家族,分别封装起来,让它们
之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

商场收银时如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没有错,但算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,这就是变化点,而封装变化点是我们面向对象的一种很重要的思维方式

策略模式实现

新增CashContext类:

public class CashContext {

    private CashSuper cs; //声明一个CashSuper对象

    //通过构造方法,传入具体的收费策略
    public CashContext(CashSuper cashSuper){
        this.cs = cashSuper;
    }

    public double getResult(double price,int num){
        //根据收费策略的不同,获得计算结果
        return this.cs.acceptCash(price,num);
    }
}

客户端相应修改

public static void main(String[] args) {
        double price = 0d;        //商品单价
        int num = 0;              //商品购买数量
        double totalPrices = 0d;  //当前商品合计费用
        double total = 0d;        //总计所有商品费用
        int discount = 1;         //商品销售模式

        Scanner sc = new Scanner(System.in);

        do {
            System.out.println("请输入商品销售模式 1.原价 2.八折 3.七折");
            discount = Integer.parseInt(sc.nextLine());
            System.out.println("请出入商品单价:");
            price = Double.parseDouble(sc.nextLine());
            System.out.println("请输入商品数量:");
            num = Integer.parseInt(sc.nextLine());
            System.out.println();

            if (price > 0 && num > 0) {

                CashContext cc = null;
                //根据用户输入,将对应的策略对象作为参数传入CashContext对象中
                switch (discount){
                    case 1:
                        cc = new CashContext(new CashNormal());
                    case 2:
                        cc = new CashContext(new CashRebate(0.8d));
                    case 3:
                        cc = new CashContext(new CashRebate(0.7d));
                    case 4:
                        cc = new CashContext(new CashReturn(300d,100d));
                }
                //通过Context的getResult方法的调用,可以得到收取费用的结果
                //让具体算法与客户进行隔离
                totalPrices = cc.getResult(price,num);
                total = total + totalPrices;


                System.out.println();
                System.out.println("销售模式为:" + discount + "单价:" + price + "元 数量:" + num + "合计:" + totalPrices + "元");
                System.out.println();

                System.out.println("总计:" + total + "元");
                System.out.println();
            }

        } while (price > 0 && num > 0);
    }

但是不难看出来现在又回到了在客户端判断代码逻辑了,所以我们要优化一下

策略与简单工厂结合

改造CashContext

public class CashContext {

    private CashSuper cs; //声明一个CashSuper对象

    //通过构造方法,传入具体的收费策略
    public CashContext(int cashType){
        switch (cashType){
            case 1:
                this.cs = new CashNormal();
                break;
            case 2:
                this.cs = new CashRebate(0.8d);
                break;
            case 3:
                this.cs = new CashRebate(0.7d);
                break;
            case 4:
                this.cs = new CashReturn(300d, 100d);
                break;
        }
    }

    public double getResult(double price,int num){
        //根据收费策略的不同,获得计算结果
        return this.cs.acceptCash(price,num);
    }
}

修改客户端

public static void main(String[] args) {
        double price = 0d;        //商品单价
        int num = 0;              //商品购买数量
        double totalPrices = 0d;  //当前商品合计费用
        double total = 0d;        //总计所有商品费用
        int discount = 1;         //商品销售模式

        Scanner sc = new Scanner(System.in);

        do {
            System.out.println("请输入商品销售模式 1.原价 2.八折 3.七折");
            discount = Integer.parseInt(sc.nextLine());
            System.out.println("请出入商品单价:");
            price = Double.parseDouble(sc.nextLine());
            System.out.println("请输入商品数量:");
            num = Integer.parseInt(sc.nextLine());
            System.out.println();

            if (price > 0 && num > 0) {


                //根据用户输入,将对应的策略对象作为参数传入CashContext对象中
                CashContext cc = new CashContext(discount);
                //通过Context的getResult方法的调用,可以得到收取费用的结果
                //让具体算法与客户进行隔离
                totalPrices = cc.getResult(price,num);
                total = total + totalPrices;


                System.out.println();
                System.out.println("销售模式为:" + discount + "单价:" + price + "元 数量:" + num + "合计:" + totalPrices + "元");
                System.out.println();

                System.out.println("总计:" + total + "元");
                System.out.println();
            }

        } while (price > 0 && num > 0);
    }

策略模式解析

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合

总结

//简单工厂模式的用法
CashSuper csuper = CashFactory.createCashAccept(discount);
totalPrices = csuper.acceptCash(price,num);

//策略模式与简单工厂结合的用法
CashContext cc = new CashContext(discount);
totalPrices = cc.getResult(price,num);

简单工厂模式我需要让客户端认识两个类,CashSuper和CashFactory,而策略模式与简单工厂结合的用法,客户端就只需要认识一个类CashContext就可以了。耦合更加降低

本文中所有代码收录在gitee仓库中,仓库地址: 点我我是仓库地址有需要的小伙伴可以自取

  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值