设计模式_2:策略模式
来源:互联网 发布:linux ln 删除 编辑:程序博客网 时间:2024/05/17 23:31
现在出现了一个需求,编写一个超市收银台软件,可以根据商品的价格统计出消费者应付金额,先来没头脑的一顿操作:
import java.util.Scanner;public class Main { private static double total = 0; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); do { System.out.println("请输入商品价格:"); double price = scanner.nextDouble(); System.out.println("请输入商品数量:"); int count = scanner.nextInt(); total += price * count; System.out.println("单价:"+price+",数量:"+count+",合计:"+price*count); System.out.println("现在总价为"+total); System.out.println("还要计算吗? 1继续,0退出"); } while (scanner.nextInt() == 1); System.out.println("程序已退出"); }}
运行示例:
请输入商品价格:
1.25请输入商品数量:
3
单价:1.25,数量:3,合计:3.75
现在总价为3.75
还要计算吗? 1继续,0退出
1
请输入商品价格:
2
请输入商品数量:
3
单价:2.0,数量:3,合计:6.0
现在总价为9.75
还要计算吗? 1继续,0退出
0
程序已退出
简单暴力地完成了任务!突然需求有改,需要增加一个可以为某件商品活动打折的功能,可以再无头脑的干一波:
import java.util.Scanner;public class Main { private static double total = 0; private static String[] cashRates = {"正常", "1折", "2折", "3折", "4折", "5折", "6折", "7折", "8折", "9折", "正常"}; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); do { System.out.println("请输入商品价格:"); double price = scanner.nextDouble(); System.out.println("请输入商品数量:"); int count = scanner.nextInt(); System.out.println("请输入该商品折扣(0正常,9为9折,8为八折,依次类推):"); int cashRate = scanner.nextInt(); cashRate = cashRate == 0 ? 10 : cashRate; double sum = price * count * cashRate / 10; total += sum; System.out.println("折扣:"+cashRates[cashRate]+",单价:"+price+",数量:"+count+",合计:"+sum+",现在总价为"+total); System.out.println("还要计算吗? 1继续,0退出"); } while (scanner.nextInt() == 1); System.out.println("程序已退出"); }}
运行示例(小数点问题先忽略,主要是为了说策略模式):
请输入商品价格:
1.25
请输入商品数量:
3
请输入该商品折扣(0正常,9为9折,8为八折,依次类推):
8
折扣:8折,单价:1.25,数量:3,合计:3.0,现在总价为3.0
还要计算吗? 1继续,0退出
1
请输入商品价格:
2.3
请输入商品数量:
3
请输入该商品折扣(0正常,9为9折,8为八折,依次类推):
9
折扣:9折,单价:2.3,数量:3,合计:6.209999999999999,现在总价为9.209999999999999
还要计算吗? 1继续,0退出
0
程序已退出
需求又要改了,又要添加一个现金返利活动(像满100返现50的活动),面对频繁修改的需求,就不适合按上面的步骤来改了,于是可以考虑换成上一章节的简单工厂模式来实现一下:
import java.util.Scanner;public class Main { private static double total = 0; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); do { System.out.println("请输入该商品活动(normal/rate/benefit):"); String type = scanner.next(); System.out.println("输入活动参数param_1:"); //这里输入两个参数normal可以随便输,rate要输入第一个参数也就是折扣,benefit分别输入返利要求金额和返利金额大小 double param_1 = scanner.nextDouble(); System.out.println("输入活动参数param_2:"); double param_2 = scanner.nextDouble(); Cash cash = CashFactory.createCash(type, param_1, param_2); System.out.println("请输入商品价格:"); double price = scanner.nextDouble(); System.out.println("请输入商品数量:"); int count = scanner.nextInt(); double sum = cash.acceptCash(price, count); total += sum; System.out.println("优惠类型:" + cash.getClass().getSimpleName() + ",单价:" + price + ",数量:" + count + ",合计:" + sum + ",现在总价为" + total); System.out.println("还要计算吗? 1继续,0退出"); } while (scanner.nextInt() == 1); System.out.println("程序已退出"); }}//抽象的收费手段abstract class Cash { public abstract double acceptCash(double price, int count);}//普通收费手段class NormalCash extends Cash { @Override public double acceptCash(double price, int count) { return price * count; }}//打折收费手段class RateCash extends Cash { private double rate; public RateCash(double rate){ this.rate = rate; } @Override public double acceptCash(double price, int count) { return price * count * rate; }}//返现收费手段class BenefitCash extends Cash { private double moneyCondition; private double moneyReturn; public BenefitCash(double moneyCondition, double moneyReturn){ this.moneyCondition = moneyCondition; this.moneyReturn = moneyReturn; } @Override public double acceptCash(double price, int count) { if ( (price * count) / moneyCondition > 1 ) return price * count - (int)((price * count) / moneyCondition) * moneyReturn; return price * count; }}//收费手段类工厂(不要看成现钞工厂( ⊙ o ⊙ ))class CashFactory { public static Cash createCash(String cashType, double... params){ Cash cash = null; switch (cashType) { case "normal": cash = new NormalCash(); break; case "rate": cash = new RateCash((params[0])); break; case "benefit": cash = new BenefitCash(params[0], params[1]); break; default: System.out.println("找不到该优惠活动"); } return cash; }}
运行示例:
请输入该商品活动(normal/rate/benefit):
benefit
输入活动参数param_1:
200
输入活动参数param_2:
50
请输入商品价格:
250
请输入商品数量:
1
优惠类型:BenefitCash,单价:250.0,数量:1,合计:200.0,现在总价为200.0
还要计算吗? 1继续,0退出
1
请输入该商品活动(normal/rate/benefit):
rate
输入活动参数param_1:
0.8
输入活动参数param_2:
0
请输入商品价格:
256
请输入商品数量:
2
优惠类型:RateCash,单价:256.0,数量:2,合计:409.6,现在总价为609.6
还要计算吗? 1继续,0退出
0
程序已退出
简单工厂模式较好地解决了需求频繁更改的情况,但某种算法改动时还是需要改动对应的工厂代码,有没有一种更优雅的方法呢,即使其更松耦合更易维护?该是策略模式出场的时候了:
先写业务逻辑层
//上下文,用来维护一个收费策略的引用class CashContext { private Cash cash; public CashContext(Cash cash){ this.cash = cash; } public double acceptCash(double price, int count){ return cash.acceptCash(price, count); }}//抽象的收费手段abstract class Cash { public abstract double acceptCash(double price, int count);}//普通收费手段class NormalCash extends Cash { @Override public double acceptCash(double price, int count) { return price * count; }}//打折收费手段class RateCash extends Cash { private double rate; public RateCash(double rate){ this.rate = rate; } @Override public double acceptCash(double price, int count) { return price * count * rate; }}//返现收费手段class BenefitCash extends Cash { private double moneyCondition; private double moneyReturn; public BenefitCash(double moneyCondition, double moneyReturn){ this.moneyCondition = moneyCondition; this.moneyReturn = moneyReturn; } @Override public double acceptCash(double price, int count) { if ( (price * count) / moneyCondition > 1 ) return price - (int)(price / moneyCondition) * moneyReturn; return price; }}
上面代码通过用一个Context类来管理策略类,但是面临一个问题,这样写的话main方法又要和最初那样写一大串switch分支来解决策略的选择了,有一个比较好的方法就是通过策略+工厂模式,改良后的最终代码如下:
import java.util.Scanner;public class Main { private static double total = 0; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); do { System.out.println("请输入该商品活动(normal/rate/benefit):"); String type = scanner.next(); System.out.println("输入活动参数param_1:"); //这里输入两个参数normal可以随便输,rate要输入第一个参数也就是折扣,benefit分别输入返利要求金额和返利金额大小 double param_1 = scanner.nextDouble(); System.out.println("输入活动参数param_2:"); double param_2 = scanner.nextDouble(); CashContext cashContext = new CashContext(type, param_1, param_2); System.out.println("请输入商品价格:"); double price = scanner.nextDouble(); System.out.println("请输入商品数量:"); int count = scanner.nextInt(); double sum = cashContext.acceptCash(price, count); total += sum; System.out.println("优惠类型:" + cashContext.getCash().getClass().getSimpleName() + ",单价:" + price + ",数量:" + count + ",合计:" + sum + ",现在总价为" + total); System.out.println("还要计算吗? 1继续,0退出"); } while (scanner.nextInt() == 1); System.out.println("程序已退出"); }}//上下文,用来维护一个收费策略的引用class CashContext { private Cash cash; public CashContext(String cashType, double... params){ switch (cashType) { case "normal": this.cash = new NormalCash(); break; case "rate": this.cash = new RateCash((params[0])); break; case "benefit": this.cash = new BenefitCash(params[0], params[1]); break; default: System.out.println("找不到该优惠活动"); } } public double acceptCash(double price, int count){ return cash.acceptCash(price, count); } public Cash getCash() { return cash; }}//抽象的收费手段abstract class Cash { public abstract double acceptCash(double price, int count);}//普通收费手段class NormalCash extends Cash { @Override public double acceptCash(double price, int count) { return price * count; }}//打折收费手段class RateCash extends Cash { private double rate; public RateCash(double rate){ this.rate = rate; } @Override public double acceptCash(double price, int count) { return price * count * rate; }}//返现收费手段class BenefitCash extends Cash { private double moneyCondition; private double moneyReturn; public BenefitCash(double moneyCondition, double moneyReturn){ this.moneyCondition = moneyCondition; this.moneyReturn = moneyReturn; } @Override public double acceptCash(double price, int count) { if ( (price * count) / moneyCondition > 1 ) return price * count - (int)((price * count) / moneyCondition) * moneyReturn; return price * count; }}
这样,又将实例化策略的的具体过程由客户端转移到了Context类中
现在对比一下简单工厂模式和策略+工厂模式的代码:
工厂模式:
Cash cash = CashFactory.createCash(type, param_1, param_2);策略+工厂模式:
CashContext cashContext = new CashContext(type, param_1, param_2);主要区别就在此:
采用策略+工厂模式,main方法中只需认识CashContext就行了,连父类Cash都不需要认识。而纯工厂模式main需要知道Cash和CashFactory两个类。因此采用策略+工厂模式可以进一步实现解耦
总结:
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,策略模式可以以相同的方式调用所有的方法,从而减少了算法类和与使用算法类之间的耦合
另外,策略模式可以简化单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试
策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性
- 设计模式_2:策略模式
- 设计模式——策略模式_2
- Boolan 设计模式_2
- 设计模式-策略模式
- 设计模式:策略模式
- 设计模式-策略模式
- 设计模式 - 策略模式
- 设计模式-策略模式
- 设计模式-----策略模式
- 设计模式 策略模式
- 设计模式-策略模式
- 设计模式-【策略模式】
- 设计模式-----策略模式
- 设计模式-策略模式
- 设计模式--策略模式
- 设计模式- 策略模式
- 设计模式- 策略模式
- 设计模式------策略模式
- 参数化查询'(@StudentNo nvarchar(3),@CardID nvarchar(1),@UserID nvarchar(400'需要参数@UserID'),但是未提供该参数。)
- Python——list常见用法
- gdb调试
- 路径分析
- 滤镜 黑白图片
- 设计模式_2:策略模式
- Jmeter常用数据库设置
- Android DalivkVM与JVM的比较
- 关于矩阵快速幂の简单粗暴の理解
- Java 学习资源
- QtQuick 日期滚动控件
- Fibsieve`s Fantabulous Birthday
- 二叉树链式建立(按满二叉树方式建立)
- 当使用微软365时总是出现:“程序出现问题”