策略模式概述
策略模式,即与解决问题的策略有关的模式,该模式旨在更好的实现策略的实现。策略模式分为三个部分:环境、抽象策略角色、具体策略角色。策略模式能使得更好地管理和使用一类算法。
- 环境(context):用于确定对应的具体策略角色。并将解决策略的功能最终放在环境context中(只要向下调用功能即可)。
- 抽象策略角色:能完成一种问题的解决的各种算法的范称,通常为接口,定义能解决问题的功能,但是不实现,具体的算法的步骤由实现类解决。
- 具体策略角色:实现抽象策略角色,要完成具体的解决问题的功能的角色。
代码实现举例
如果一家商场能让顾客办会员卡以达到优惠的目的,而会员卡分为两种:八折卡、满减卡(每满三百减五十)。要求输入原价格和优惠方式,得到优惠后的价格。
分析,这里的抽象策略角色应该为优惠方式,而具体策略角色有三个:八折、满减、普通,而环境(通常用context表示)用于确定对应具体的优惠方式
抽象策略角色(优惠方式)
/**
* 表示优惠的算法,具体优惠方式由实现类定义
*/
public interface Concession {
//计算优惠后的价格的功能
Double doConcession(Double price);
}
具体策略角色(优惠方式的不同具体实现)
普通:
/**
* 没有优惠卡的计算方法
*/
public class NormalConcession implements Concession {
@Override
public Double doConcession(Double price) {
return price;
}
}
八折卡:
/**
* 有八折卡的计算方法
*/
public class RebateConcession implements Concession {
@Override
public Double doConcession(Double price) {
return price * 0.8;
}
}
满减卡:
/**
* 有满减卡的计算方法(每满三百减五十)
*/
public class ReturnConcession implements Concession {
@Override
public Double doConcession(Double price) {
return price - ( (int)(price / 300) * 50 );
}
}
普通策略模式的环境(context)
/**
* 普通策略模式的Context类,用于决定不同的算法
*/
public class ConcessionContext {
private Concession concession;
public ConcessionContext(Concession concession){
this.concession = concession;
}
public Double doConcession(Double price){
return concession.doConcession(price);
}
}
通过传入不同的具体策略模式的对象,来决定其成员变量concession是怎样的策略。并且将策略的功能最终放在环境中,业务代码只要调用环境的功能的方法即可。
主要业务代码
public class Main {
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("请输入原价格");
double price = scan.nextDouble();
System.out.println("请输入优惠方式");
String method = scan.next();
ConcessionContext context = null;
switch (method){
case "普通":
context = new ConcessionContext(new NormalConcession());
break;
case "八折卡":
context = new ConcessionContext(new RebateConcession());
break;
case "满减卡":
context = new ConcessionContext(new ReturnConcession());
break;
default:
context = null;
break;
}
if (context == null){
throw new Exception("方式错误");
}
Double concession = context.doConcession(price);
System.out.println("优惠后价格为:" + concession);
}
但是这种方式有一些缺点:
- 具体策略的选择在业务代码中进行,当修改底层时(比如增加、删除、更新了策略)也要修改业务代码
- 业务代码中要实例化各种具体策略对象,使得代码繁杂,而且个人感觉这样使得环境context有点多余,在这种情况下,context只是起了一个没有中间作用的类,判断完后直接使用具体实现策略的对象调用方法差别也不大
策略模式与简单工厂模式相结合
结合的要点是,让环境context起到一个工厂类的效果,来完成实例化。并且,让context去完成具体实现策略的判断后再作对应,我们只要传入要选择的策略的标识即可(比如传入目标策略的名字,让context根据此名字来实例化对应的策略对象)。
升级版的context
/**
* 策略者模式与简单工厂模式相结合
*/
public class ConcessionContextPlus {
Concession context = null;
public ConcessionContextPlus(String method) throws Exception {
switch (method){
case "普通":
context = new NormalConcession();
break;
case "八折卡":
context = new RebateConcession();
break;
case "满减卡":
context = new ReturnConcession();
break;
default:
context = null;
break;
}
if (context == null) {
throw new Exception("方式错误");
}
}
public Double doConcession(Double price){
return context.doConcession(price);
}
}
升级后的业务代码
/**
* 升级版业务(非常简洁,可读性高)
*/
public class MainPlus {
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("请输入原价格");
double price = scan.nextDouble();
System.out.println("请输入优惠方式");
String method = scan.next();
//实际上两步就完成了策略
//传入优惠方式
ConcessionContextPlus concessionContextPlus = new ConcessionContextPlus(method);
//传入原价格
Double concession = concessionContextPlus.doConcession(price);
System.out.println("优惠后价格为" + concession);
}
}
升级后的业务代码明显简单了很多。
总结
- 策略模式能更好地管理和使用一类算法,在做算法增删改时,无需改动业务代码,只要对底层进行修改。使得代码的耦合度低,封装性强。
- 策略模式结合简单工厂模式,让context起到工厂的作用,能让业务代码更为简洁,也更易于理解和使用。比起普通的策略模式,耦合度更低,封装性更强。