这段时间又开始着手看下设计模式了 之前在看netty的时候 用到了观察者模式 (Future) 然后觉得设计模式还是比较模糊
不知道什么场景用什么模式 在后面将以前的项目和看的源码中用到的设计模式 在这些总结中慢慢体现出来。比如之前做的
道具系统.活动等(这里指的是游戏里面的 后面一次会有相应的总结)........
在实践中,我们发现几乎用它来封装分析过程中在不同时间不同业务的需求规则 就可以考虑使用策略模式了
例如:在举出一个简单的商城商品促销活动的案例
一 : 需求(1)
三月某些商品按照8折销售 其他按照正常价格出售
我们来看看小白的Method1包下的的实现
package 策略模式.Method1; import org.junit.Test; public class Method1 { public int number; public int money; public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } //正常商品 public double calculete(int num, int money) { return num * money; } public double calculateDisCount(int num, int money){ return num*money*0.8; } @Test public void test(){ //正常商品 System.out.printf("正常商品"+calculete(2,10)+""); //打8折商品 System.out.println("打折商品"+calculateDisCount(2,10)); } }
上述代码确实可以满足 打折商品的需求 那么如果 4月份老板要再做一个满300送100的返利活动呢 ?
我们的小白是否又是在Method1类下面加一个返利活动的方法呢?同理 咋们扩展下 满500送200 满700送300 满1000送300 ....依次类推 显然我们我们这样写死方法 实现具体需求这样不是很理智的 那么我们试着将活动的不变性写成接口 具体计算由子类去实现
带着这些可能的不确定性 我们需要将这种不确定的算法导致最后金额的算法抽象出来 这样,每增加一个活动 我们需要去实现对应的抽象即可 我们看下需求2
二:需求(2)
四月增加满300 返100 和满500的促销活动
我们再来看看Method2包下的实现
我们看看代码实现如下
抽象出父类 只提供最后的价格 中间的计算逻辑由子类去实现
package 策略模式.Method2; public abstract class Calculte { //最后的实际价格 public abstract double totalMoeny(int money); }
打折的实现类
package 策略模式.Method2; /*打折商品(包含正常商品)*/ public class Discount extends Calculte { private double discountRate; public Discount(double discount) { this.discountRate = discount; } @Override public double totalMoeny(int money) { return money * discountRate; } }
返利的实现类
package 策略模式.Method2; public class ReturnM extends Calculte { private double contidionMoney; private double returnMoney; public ReturnM(double contidionMoney, double returnMoney) { this.contidionMoney = contidionMoney; this.returnMoney = returnMoney; } @Override public double totalMoeny(int money) { if(money>=contidionMoney){ return money-returnMoney; } return money; } }
构建简单工厂得到具体的实现类
package 策略模式.Method2; public class CashFactory { public static Calculte createAdpater(String type){ Calculte calculte=null; switch (type){ case "正常收费": calculte=new Discount(1); break; case "打8折": calculte=new Discount(0.8); break; case "返利": calculte=new ReturnM(300,100); break; } return calculte; } }
我们可以简单测试一下
package 策略模式.Method2; import org.junit.Test; public class test { @Test public void testDiscount(){ //来个正常收费模式 Calculte calculte= CashFactory.createAdpater("正常收费"); System.out.println("正常收费模式------"+calculte.totalMoeny(300)); //来个8折收费模式 Calculte calculte1= CashFactory.createAdpater("打8折"); System.out.println("8折收费模式------"+calculte1.totalMoeny(300)); //来个满300返100 Calculte calculte2= CashFactory.createAdpater("返利"); System.out.println("满300返100模式------"+calculte2.totalMoeny(300)); } }
很明显我们定义了一个抽象类 子类包括了现有的所有活动具体的细节 然后用工厂类创建出对应的具体实现 我们观察一下
工厂类
switch (type){
case "正常收费":
calculte=new Discount(1);
break;
case "打8折":
calculte=new Discount(0.8);
break;
case "返利":
calculte=new ReturnM(300,100);
break;
}
假如5月我们需要增加一个增加一个满500送200 或者增加一个打7折的活动 我们就在这个工厂方法里面加上
case "打5折":
calculte=new Discount(0.5);
break; 或者
case "返利200":
calculte=new ReturnM(500,200);
break;
当然由简单工厂去创建具体的实现 是可以满足具体的实现 但是每次都需要维护更新工厂,以至于代码需要重新编译部署所以并不是最好的处理方式 其实这里还是有优化的余地 我们发现每次获取一个打折的商品都需要通过工厂类CashFactory 获取到对应的实现类调用对应的算法来拿到想要的结果 其实对于调用者并不是很关心中间的一些过程 他们只关心的是 我给你什么商品 你告诉我多少价格就ok不是吗?
我们做一下简单的优化 看下 Method3包下的实现
Calculte Discount ReturnM这三个类均不需要做改变 我们删除掉CashFactory类 添加一个Context类如下
package 策略模式.Method3;
//上下文(管理实现)
public class Context {
private Calculte ca;
public Context(String type) {
switch (type) {
case "正常收费":
ca = new Discount(1);
break;
case "打8折":
ca = new Discount(0.8);
break;
case "返利":
ca = new ReturnM(300, 100);
break;
}
}
public double totalMoeny(int money) {
return ca.totalMoeny(money);
}
}
测试如下
package 策略模式.Method3;
import org.junit.Test;
public class test {
@Test
public void testDiscount(){
//来个正常收费模式
Context calculte= new Context("正常收费");
System.out.println("正常收费模式------"+calculte.totalMoeny(300));
//来个8折收费模式
Context calculte1= new Context("打8折");
System.out.println("8折收费模式------"+calculte1.totalMoeny(300));
//来个满300返100
Context calculte2= new Context("返利");
System.out.println("满300返100模式------"+calculte2.totalMoeny(300));
}
}
我们可以看出我们用Context替代了Method2的CashFactory 可以让程序减少暴露更多的类 我们看下Method2中的test
在Method3中 我们只需要通过Context传入对应的参数 即可得到相应的结果。但是我们这个模式针对可变的需求还是没有很好的 解决 比如我们加一个满700返200 打5折的这种需求时候 我们必须在Context中加case 和写Calculte对应的实现 。。。那么有没有更好的解决方案(这个我们需要用到反射技术) 后面我们在加上 这里我们只是针对当前的业务能尽可能的完善