Java 策略模式
Posted 小女子不才~~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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: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:测试主类
* @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类的构造方法即可。有新需求是肯定要改动的,使用策略模式会降低改动成本。这也是策略模式的好处之一了。
设计模式不光是纸上谈兵,要分析需求,根据需求使用对应的设计模式。
后记:天道好轮回,苍天饶过谁~~~,欠下的迟早是要还的~~
以上是关于Java 策略模式的主要内容,如果未能解决你的问题,请参考以下文章