Java 策略模式

前记:欠下的迟早是要还的~~~还债第四篇~

需求

跟以前一样,先说需求:最近公司要做一个小商城,商城里的产品有不同的销售价格,有得是原价,有的是打7折的,有的是打5折的。

代码展示

如果需求一直不变,最简单粗暴的写法是这样的:

package signModel.strategy;
/**
 * @description:最简单粗暴的写法
 * @see:signModel.strategy.simpleFactory
 * @createTime:2021/8/9 17:10
 * @version:1.0
 */
public class simple {
    public static void main(String[] args) {
        String type="7折";
        double money = 100.0;  //总价
        double rs=  getResultAmt( type,money);
    }
    private static double getResultAmt(String type, double money) {
        double result=0;
        switch (type){
            case "原价":
                return money;
            case "7折":
                return money*0.7;
            case "5折":
                return money*0.5;
        }
        return result;
    }
}

这种简单粗暴的写法,会带来一系列的麻烦,假如现在要增加一个打8折,就需要继续增加一个case,这样打5折,打7折,打8折的代码其实是一样的,重复代码很多。可以给提出来一个打折的函数,另外如果要增加一个满减的新需求,满200-20,这样的话,满400就得减40,这个满减就不能在case里写了,这个得写个函数才好使。
加上满减以后,计算价格的方法就会分为3类,原价,打折,满减。
上次的设计模式讲的是简单工厂模式,碰到这种,首先想到可以用来消除代码种的case和if-else.所以这里我们也用简单工厂测试来进行改造。简单工厂模式呢就是先写一个计算金额的抽象类,子类重写父类的计算金额的方法。另外还需要一个工厂类。
直接上代码:

/**
 * @description:简单工厂模式抽象父类
 * @see:signModel.strategy.simpleFactory
 * @createTime:2021/8/9 17:35
 * @version:1.0
 */
public abstract class CashSuper {
    public  abstract double geTotalCash(double money);
}

/**
 * @description:子类-原价商品
 * @see:signModel.strategy.simpleFactory
 * @createTime:2021/8/9 17:37
 * @version:1.0
 */
public class Normal extends CashSuper {

    @Override
    public double geTotalCash(double money) {
        return money;
    }
}


/**
 * @description:子类2-打折商品
 * @see:signModel.strategy.simpleFactory
 * @createTime:2021/8/9 18:18
 * @version:1.0
 */
public class Rebate extends CashSuper {
    private double rebate; //几折
    public Rebate(double rebate) {
        this.rebate = rebate;
    }
    @Override
    public double geTotalCash(double money) {
        return money* rebate/10;
    }
    public double getRebate() {
        return rebate;
    }
    public void setRebate(double rebate) {
        this.rebate = rebate;
    }
}


/**
 * @description:子类-满减方法类
 * @see:signModel.strategy.simpleFactory
 * @createTime:2021/8/9 18:21
 * @version:1.0
 */
public class FullMinus extends CashSuper {
    private  double fullMoney;
    private double minusMoney;

    public FullMinus(double fullMoney, double minusMoney) {
        this.fullMoney = fullMoney;
        this.minusMoney = minusMoney;
    }
    @Override
    public double geTotalCash(double money) {
        if(money>=fullMoney){
            return money-Math.floor(money/this.fullMoney)*minusMoney;
        }
        return money;
    }
    public double getMinusMoney() {
        return minusMoney;
    }
    public void setMinusMoney(double minusMoney) {
        this.minusMoney = minusMoney;
    }
}

/**
 * @description:
 * @see:signModel.strategy.simpleFactory
 * @createTime:2021/8/9 18:36
 * @version:1.0
 */
public class CashFactory {
    public static CashSuper getCash(String type){
        CashSuper cashSuper=null;
        switch (type){
            case "7折":
                cashSuper=new Rebate(7.0);
                break;
            case "满减":
                cashSuper= new FullMinus(300.0,20.0);
                break;
            case "原价":
                cashSuper=new Normal();
        }
        return cashSuper;
    }
}


/**
 * @description:测试主类,可直接运行看效果
 * @see:signModel.strategy.simpleFactory
 * @createTime:2021/8/9 18:42
 * @version:1.0
 */
public class Main {

    public static void main(String[] args) {
        double money = 300.0;  //总价

        String type="7折";
        CashSuper cash = CashFactory.getCash(type);
        double result = cash.geTotalCash(money);
        System.out.println("打7折的价格:"+result);

        String type1="满减";
        CashSuper cashSuper=CashFactory.getCash(type1);
        double totalCash = cashSuper.geTotalCash(money);
        System.out.println(totalCash);

    }
}

代码测试运行结果:
在这里插入图片描述

策略模式

以上是使用简单工厂模式的完成的写法,现在我们转换到主题策略模式上来,首先说一下什么是策略模式:
大话设计模式种对策略模式的定义:它定义了算法家族,分别封装起来,让他们直接可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。
在上边的例子中,商场的促销方式,无论是打折还是促销,其实都是一些算法,用工厂来生成算法对象,这也是可以的,但是算法本身只是一种策略,最重要是的是这些算法是随时都可能相互替换的,这就是变化点,封装变化点是我们面向对象编程的一种很重要的思维方式。现在来看看策略模式的基本结构:
首先上一下UML图,一目了然:
在这里插入图片描述
介绍下上边图中的各个角色:
抽象策略角色(Strategy类):这是一个抽象角色,通常由一个接口或者抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略角色(StrategyNormal,StrategyRebate,StrategyFullMunus类,即Strategy类的子类):包含了具体的算法和行为。
环境角色类(Context类):持有一个Strategy类的引用。
下边是使用策略模式实现的需求的代码:

/**
 * @description:策略类,定义所有支持算法的公共接口
 * @see:signModel.strategy.strategy
 * @createTime:2021/8/10 10:36
 * @version:1.0
 */
public abstract class Strategy {
    public  abstract double geTotalCash(double money);
}

/**
 * @description:具体策略1-正常算法
 * @see:signModel.strategy.strategy
 * @createTime:2021/8/10 10:39
 * @version:1.0
 */
public class StrategyNormal extends Strategy {
    @Override
    public double geTotalCash(double money) {
        return money;
    }
}

/**
 * @description:具体策略2-打折算法类
 * @see:signModel.strategy.strategy
 * @createTime:2021/8/10 10:40
 * @version:1.0
 */
public class StrategyRebate extends Strategy {
    private double rebate; //几折

    //有参构造
    public StrategyRebate(double rebate) {
        this.rebate = rebate;
    }

    @Override
    public double geTotalCash(double money) {
        return money* rebate/10;
    }

    public double getRebate() {
        return rebate;
    }

    public void setRebate(double rebate) {
        this.rebate = rebate;
    }
}


/**
 * @description:具体策略3-满减算法
 * @see:signModel.strategy.strategy
 * @createTime:2021/8/10 10:42
 * @version:1.0
 */
public class StrategyFullMinus extends Strategy {
    private  double fullMoney;
    private double minusMoney;

    public StrategyFullMinus(double fullMoney, double minusMoney) {
        this.fullMoney = fullMoney;
        this.minusMoney = minusMoney;
    }

    @Override
    public double geTotalCash(double money) {
        if(money>=fullMoney){
            return money-Math.floor(money/this.fullMoney)*minusMoney;
        }
        return money;
    }

    public double getMinusMoney() {
        return minusMoney;
    }

    public void setMinusMoney(double minusMoney) {
        this.minusMoney = minusMoney;
    }
}

/**
 * @description:环境上下文角色-维护一个对Strategy类的引用
 * @see:signModel.strategy.strategy
 * @createTime:2021/8/10 10:48
 * @version:1.0
 */
public class Context {
    private Strategy strategy;
    //有参构造,直接传入具体的算法对象
   
    //调用策略种的具体方法并返回结果。
    public double runMethod(double money){
        double cash = this.strategy.geTotalCash(money);
        return cash;
    }
}


测试主类:
/**
 * @description:测试主类
 * @see:signModel.strategy.strategy
 * @createTime:2021/8/10 10:56
 * @version:1.0
 */
public class Main {
    public static void main(String[] args) {
        double money = 300.0;  //总价

        String type="7折";
        double rs=  getResultAmt( type,money);
        System.out.println("打7折后的总价格:"+rs);

        String type1="满减";
        double rs1=  getResultAmt( type1,money);
        System.out.println("满减后的总价格:"+rs1);
    }
    private static double getResultAmt(String type, double money) {
        Context context = null;
        switch (type) {
            case "原价":
                context = new Context(new StrategyNormal());
                break;
            case "7折":
                context = new Context(new StrategyRebate(7.0));
                break;
            case "满减":
                context = new Context(new StrategyFullMinus(300.0,20));
                break;
        }
        return context.runMethod(money);
    }
}

运行结果:
在这里插入图片描述

策略模式+简单工厂

当然了,这样写,你会发现,测试主类里又写了一堆switch-case语句,只是把算法这部分给提了出来,看上去不如简单工厂模式封装的好。既然这样,那我们把简单工厂模式和策略模式给结合一下,把switch-case 这部分代码放到Context类中,这样,Context代码和测试主类代码如下:

/**
 * @description:环境上下文角色-维护一个对Strategy类的引用
 * @see:signModel.strategy.strategy
 * @createTime:2021/8/10 10:48
 * @version:1.0
 */
public class Context {
    private Strategy strategy;
    //有参构造,直接传入具体的算法对象
    public  Context(String type) {
        switch (type) {
            case "原价":
                this.strategy = new StrategyNormal();
                break;
            case "7折":
                this.strategy = new StrategyRebate(7.0);
                break;
            case "满减":
                this.strategy =new StrategyFullMinus(300.0,20);
                break;
        }
    }
    //调用策略种的具体方法并返回结果。
    public double runMethod(double money){
        double cash = this.strategy.geTotalCash(money);
        return cash;
    }
}

测试主类:
/**
 * @description:测试主类
 * @see:signModel.strategy.strategy
 * @createTime:2021/8/10 10:56
 * @version:1.0
 */
public class Main {
    public static void main(String[] args) {
        double money = 300.0;  //总价

        String type="7折";
        Context context=new Context(type);
        double rs= context.runMethod(money);
        System.out.println("打7折后的总价格:"+rs);

        String type1="满减";
        Context context1=new Context(type1);
        double method = context1.runMethod(money);
        System.out.println("满减后的总价格:"+method);
    }
}

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类直接的耦合。策略模式的优点就是:一方面Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于提取出这些算法种的公共功能。另一方面就是简化了单元测试,因为每个算法都有自己的类,可以通过自己的算法单独测试。
策略模式就是用来封装算法的,但在实践中,我们发现可以用他来封装几乎任何类型的规则,只要在分析的过程中听到在不同时间应用不用的业务规则,就可以考虑使用策略模式处理这种变化的可能性。上面的需求在使用策略模式以后,如果想要增加新的促销模式,只需加一个算法类,改动一下Context类的构造方法即可。有新需求是肯定要改动的,使用策略模式会降低改动成本。这也是策略模式的好处之一了。

设计模式不光是纸上谈兵,要分析需求,根据需求使用对应的设计模式。

后记:天道好轮回,苍天饶过谁~~~,欠下的迟早是要还的~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值