小弟最近在研究设计模式,准备边学边发博客,与众多大佬们交流学习,希望各位能够指出不足之处(废话不多说了,直接开花)
首先,最起码得知道为啥学习设计模式;不要被那些设计模式学了也用不上所误导,虽然设计模式的研究不是那么简单,现在所流行的设计模式也远不止23种,但是多学习设计模式,你会被那些编程大师们的编程思想所洗礼,要知道,编程并不是一个工作,而是一门哲学,面向对象编程体会颇深。在日常开发中,或许用或者不用设计模式,都能实现你的业务需求,但是,用设计模式会使你的代码日后更易于维护,(你说你代码写一次以后就不改了?牛逼,出门右拐,谢谢),往往用适当的设计模式,会使你在日后维护、升级代码上更加得心应手,能够很大几率避免牵一发而动全身的几率,(有过体会的很难受有木有,吼吼吼,改一个小功能,狠不得把整个工程重构),所以能够在日常开发中,学会常用一些符合你业务需求的设计模式,不往你是一个苦逼的程序猿,哈哈!
今天要说的设计模式使策略设计模式(Strategy):它定义了算法家族,分别封装起来,让算法与算法之间可以享胡替换,此模式的算法变化,不会影响到使用此算法的客户。
策略模式适用于什么场景呢?
主要解决的是你的系统里有很多相似的算法,你用if...else去维护很麻烦很复杂,比如说,商场逢年过节会推出打折活动;
此时让你去设计一个商场的商品购买后台,你需要实现 打八折 打五折 打一折 免费送(做梦) 满500返200........等等等各种算法,此时你就不得不去做各种判断,废话不多说,怼代码!
0.先看下策略模式的结构图
1.先写个商品类备用
package com.huangfu;
/**
* 商品类
* @author 皇甫
*/
public class Product {
private String name;
private double price;
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Product() {
}
public Product(String name, double price) {
this.name = name;
this.price = price;
}
}
2. 定义收费接口
package com.huangfu;
/**
* 商场的收费接口类,应对商场的各种打折方式
* @author 皇甫
*/
public interface Toll {
/**
* 商场的优惠方式
* @param money 原价
* @return 应付价格
*/
public double offerMethod(double money);
}
3.书写算法的实现类 实现商场的各种打折方式
package com.huangfu;
/**
* 商场正常收费 没有优惠
* @author 皇甫
*/
public class NoDiscount implements Toll{
/**
*
* @param money 原价
* @return 返回商品原价
*/
@Override
public double offerMethod(double money) {
return money;
}
}
package com.huangfu;
/**
* 商场的额返利算法 入满300返100
* @author 皇甫
*
*/
public class RebateAlgorithm implements Toll {
/**
* 商场的返利力度
*/
private double strength;
private double scale;
/**
* @param strength 商场的返利力度 比如返100
* @param scale 商场的返利满足条件 比如满足300
*/
public RebateAlgorithm(double strength, double scale) {
this.strength = strength;
this.scale = scale;
}
/**
* @param money 原价
* @return 优惠后的价格
*/
@Override
public double offerMethod(double money) {
double result = 0.0;
if(money>=scale){
result = (strength/scale) * money;
}
return money - result;
}
}
package com.huangfu;
/**
* 打折
* 商场打折的折扣算法
* @author 皇甫
*/
public class DiscountAlgorithm implements Toll{
/**
* 商场的折扣力度入打8折就是0.8
* */
private double strength;
public DiscountAlgorithm(double strength) {
this.strength = strength;
}
@Override
public double offerMethod(double money) {
return money*strength;
}
}
4.定义策略模式主体,管理各种算法
package com.huangfu;
/**
* 策略模式算法主体类
* @author 皇甫
*/
public class Content {
/**
* 将算法关联到此主题类 我的理解是将各种算法交由主题类来管理
* */
private Toll t;
/**
* 指定算法实现
* @param t
*/
public Content(Toll t) {
this.t = t;
}
/**
* 返回优惠力度
* @param money
* @return
*/
public double getResult(double money){
return t.offerMethod(money);
}
}
5.使用简单工厂管理策略主体类(你也可以不用,没人逼你;工厂模式后面会发表,持续关注喽!)
package com.huangfu;
/**
* 商品计算的简单工厂
* @author 皇甫
*/
public class CommodityFactory {
/**
* 默认正常收费 没有任何优惠力度
*/
private static Content c = new Content(new NoDiscount());
/**
* 设置优惠方式
* @param type 优惠方式
*/
public static void setOffer(String type){
switch (type){
case "打八折":
c = new Content(new DiscountAlgorithm(0.8));
case "满300返100":
c = new Content(new RebateAlgorithm(100, 300));
}
}
/**
* 返回最终的优惠价格
* @param money 应付价格
* @return 实付价格
*/
public static double getResult(double money){
return c.getResult(money);
}
}
6.大功告成,测试走起,模拟购买商品
package com.huangfu;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
/**
* 测试类
* @author 皇甫
*/
public class TestMain {
private static double price = 0.0;
private static String type = "暂无优惠";
/**
* 模拟购买
* @param args
*/
public static void main(String[] args) {
type = "满300返100";
/**
* 商场设置优惠方式 默认没有优惠
*/
//CommodityFactory.setOffer(type);
CommodityFactory.setOffer(type);
Product p1 = new Product("超大冰淇淋", 100.0);
Product p2 = new Product("风扇", 89.0);
Product p3 = new Product("口香糖", 32.5);
Product p4 = new Product("卫龙", 55.2);
Product p5 = new Product("大面筋", 215.0);
Product p6 = new Product("超大鱿鱼", 1000.5);
Product p7 = new Product("八爪鱼酱", 55.5);
Product p8 = new Product("小熊饼干", 66.6);
Map<Integer,Product> product = new HashMap<Integer,Product>();
product.put(1, p1);
product.put(2, p2);
product.put(3, p3);
product.put(4, p4);
product.put(5, p5);
product.put(6, p6);
product.put(7, p7);
product.put(8, p8);
while (true){
Set<Map.Entry<Integer, Product>> entries = product.entrySet();
for (Map.Entry<Integer, Product> entry : entries) {
System.out.println(entry.getKey()+":"+entry.getValue().getName()+" 价格"+entry.getValue().getPrice());
}
System.out.println("-------------------请输入您要购买的商品序号(现在的优惠为"+type+")-现在总计为:"+price+"元,输入0结算--------------------");
Scanner scanner = new Scanner(System.in);
int i = scanner.nextInt();
if(i==0){
break;
}
price+=product.get(i).getPrice();
System.out.println("购买成功 输入0结束购买");
}
//最终价格
double result = CommodityFactory.getResult(price);
System.out.println("实际付款"+result+"元");
}
}
7.查看结果
D:\java\jdk\bin\java.exe "-javaagent:D:\idea\IntelliJ IDEA 2018.2.1\lib\idea_rt.jar=4289:D:\idea\IntelliJ IDEA 2018.2.1\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk\jre\lib\charsets.jar;D:\java\jdk\jre\lib\deploy.jar;D:\java\jdk\jre\lib\ext\access-bridge-64.jar;D:\java\jdk\jre\lib\ext\cldrdata.jar;D:\java\jdk\jre\lib\ext\dnsns.jar;D:\java\jdk\jre\lib\ext\jaccess.jar;D:\java\jdk\jre\lib\ext\jfxrt.jar;D:\java\jdk\jre\lib\ext\localedata.jar;D:\java\jdk\jre\lib\ext\nashorn.jar;D:\java\jdk\jre\lib\ext\sunec.jar;D:\java\jdk\jre\lib\ext\sunjce_provider.jar;D:\java\jdk\jre\lib\ext\sunmscapi.jar;D:\java\jdk\jre\lib\ext\sunpkcs11.jar;D:\java\jdk\jre\lib\ext\zipfs.jar;D:\java\jdk\jre\lib\javaws.jar;D:\java\jdk\jre\lib\jce.jar;D:\java\jdk\jre\lib\jfr.jar;D:\java\jdk\jre\lib\jfxswt.jar;D:\java\jdk\jre\lib\jsse.jar;D:\java\jdk\jre\lib\management-agent.jar;D:\java\jdk\jre\lib\plugin.jar;D:\java\jdk\jre\lib\resources.jar;D:\java\jdk\jre\lib\rt.jar;D:\ideaWorkSpace\20190109\springboot_ueditor\target\classes com.huangfu.TestMain
1:超大冰淇淋 价格100.0
2:风扇 价格89.0
3:口香糖 价格32.5
4:卫龙 价格55.2
5:大面筋 价格215.0
6:超大鱿鱼 价格1000.5
7:八爪鱼酱 价格55.5
8:小熊饼干 价格66.6
-------------------请输入您要购买的商品序号(现在的优惠为满300返100)-现在总计为:0.0元,输入0结算--------------------
1
购买成功 输入0结束购买
1:超大冰淇淋 价格100.0
2:风扇 价格89.0
3:口香糖 价格32.5
4:卫龙 价格55.2
5:大面筋 价格215.0
6:超大鱿鱼 价格1000.5
7:八爪鱼酱 价格55.5
8:小熊饼干 价格66.6
-------------------请输入您要购买的商品序号(现在的优惠为满300返100)-现在总计为:100.0元,输入0结算--------------------
1
购买成功 输入0结束购买
1:超大冰淇淋 价格100.0
2:风扇 价格89.0
3:口香糖 价格32.5
4:卫龙 价格55.2
5:大面筋 价格215.0
6:超大鱿鱼 价格1000.5
7:八爪鱼酱 价格55.5
8:小熊饼干 价格66.6
-------------------请输入您要购买的商品序号(现在的优惠为满300返100)-现在总计为:200.0元,输入0结算--------------------
1
购买成功 输入0结束购买
1:超大冰淇淋 价格100.0
2:风扇 价格89.0
3:口香糖 价格32.5
4:卫龙 价格55.2
5:大面筋 价格215.0
6:超大鱿鱼 价格1000.5
7:八爪鱼酱 价格55.5
8:小熊饼干 价格66.6
-------------------请输入您要购买的商品序号(现在的优惠为满300返100)-现在总计为:300.0元,输入0结算--------------------
0
实际付款200.0元
Process finished with exit code 0
8.总结
策略模式中封装了变化,在官方定义上,策略模式就是用来封装算法的,可是在实际的业务场景中,策略模式几乎可以封装任何类型的规则。在业务分析中,只要应用到"在不同的时间应用不同的规则"这句话几乎都可以使用到策略模式,就比如上面那个例子,日后商场再增加任何算法,只需要再新增算法种类并继承优惠接口就可以了,当然需要更改工厂方法里面的switch语句(任何方法的改动都需要付出代价,后面会提到改进方法,提示:反射)就可以了,每一种算法都封装到一个类中,满足功能单一性,而且每个类负责一个算法不宜出错;所有算法继承同一个接口,方便日后的扩展,何乐而不为呢?本次就到这了,下期见!