策略模式
是定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户。
可以避免多重分之的if...else..和switch语句
应用场景
1.假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
2.一个系统需要动态的在几种算法中选择一种。
例子,京东现在要搞促销,需要几种优惠活动,那我们可以用策略模式来写。
/**
* @Author Darker
* @Descrption 京东促销活动优惠方式接口
* @Date : Created in 13:54 2020-3-12
*/
public interface JDactivity {
//促销方式
/**
* 1.原价
* 2.现金返现
* 3.优惠券减免
* 4.赠送实物
*
*/
void activity();
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 13:57 2020-3-12
*/
public class EmptyActivity implements JDactivity{
@Override
public void activity() {
System.out.println("原价");
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 13:59 2020-3-12
*/
public class CashBackActivity implements JDactivity{
@Override
public void activity() {
System.out.println("购物直接返现,返回现金到京东账号");
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 13:57 2020-3-12
*/
public class CouponsActivity implements JDactivity{
@Override
public void activity() {
System.out.println("领取优惠券,购物减免价格");
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 14:00 2020-3-12
*/
public class GiftActivity implements JDactivity {
@Override
public void activity() {
System.out.println("购物赠送礼品");
}
}
/**
* @Author Darker
* @Descrption 活动策略规则
* @Date : Created in 14:02 2020-3-12
*/
public class ActivityStrategy {
//把优惠活动作为成员变量,new哪一种子类就是哪一种优惠
JDactivity jDactivity;
public ActivityStrategy(JDactivity jDactivity){
this.jDactivity = jDactivity;
}
public void execute(){
jDactivity.activity();
}
}
运行试一试
/**
* @Author Darker
* @Descrption
* @Date : Created in 14:04 2020-3-12
*/
public class StrategyTest {
public static void main(String[] args) {
//6.18购物活动
ActivityStrategy activity618 = new ActivityStrategy(new CashBackActivity());
activity618.execute();
//双11购物优惠
ActivityStrategy activity1111 = new ActivityStrategy(new CouponsActivity());
activity1111.execute();
}
}
这就是最简单的策略模式的运用了,定义了一个算法家族(优惠家族),动态的选择一种算法(优惠)。
但是!但是!但是!!!
到了用户调用层,你会发现你又要if...else..;比如下面这样,通过前端传来的字段来生成你要的优惠活动,既然说策略模式避免if...esle..,现在不还是用了嘛,当然,一种不行可以玩结合不是,看到下面代码,活脱脱一个简单工厂模式嘛,我们完全可以改成工厂模式,用工厂来生成这个策略,顺便把工厂变成单例,还提高效率。
所以,我们给他加上代码
/**
* @Author Darker
* @Descrption
* @Date : Created in 14:40 2020-3-12
*/
public class ActivityStrategyFactory {
//构造方法私有化
private ActivityStrategyFactory(){};
//饿汉式单例
private static ActivityStrategyFactory activityStrategyFactory = new ActivityStrategyFactory();
private static Map<String,JDactivity> iocMap = new HashMap<>();
static {
iocMap.put(ActivityKey.COUPON,new CouponsActivity());
iocMap.put(ActivityKey.CASHBACK,new CashBackActivity());
iocMap.put(ActivityKey.GIFT,new GiftActivity());
}
//无优惠
private static final ActivityStrategy NON_ACTIVITY_STRATEGY = new ActivityStrategy(new EmptyActivity());
//全局唯一工厂路口
public static ActivityStrategyFactory getInstance(){
return activityStrategyFactory;
}
//工厂生成相应策略的活动
public ActivityStrategy getActivityStrategy(String activity){
ActivityStrategy activityStrategy = new ActivityStrategy(iocMap.get(activity));
return activityStrategy == null ? NON_ACTIVITY_STRATEGY:activityStrategy;
}
//活动字符串,约定>配置
private interface ActivityKey{
String COUPON = "COUPON";
String CASHBACK = "CASHBACK";
String GIFT = "GIFT";
}
}
运行看看
/**
* @Author Darker
* @Descrption
* @Date : Created in 14:04 2020-3-12
*/
public class StrategyTest {
public static void main(String[] args) {
//前端传来的活动key
String activityKey = "CASHBACK";
//生成策略工厂
ActivityStrategyFactory strategyFactory = ActivityStrategyFactory.getInstance();
ActivityStrategy activityStrategy = strategyFactory.getActivityStrategy(activityKey);
activityStrategy.execute();
}
}
你看这样就对客户端调用非常友好了不是,而且当我们增加活动的时候只需要去添加一点配置字段就行了,改起来也很快。
我们再来看看这个类图:
这样就很清晰了,我们有4个优惠策略,通过活动策略来生成对应的策略,同时用工厂来生产策略。
是不是还不过瘾,那我们再来复现一个日常中经常使用的功能,手机支付,一个非常经典的策略模式。
/**
* @Author Darker
* @Descrption 支付抽象类,包含公共判断逻辑
* @Date : Created in 16:12 2020-3-12
*/
public abstract class Paymet {
public abstract String getKeyName();
//查询余额
protected abstract double queryBalance(String uid);
//公用支付逻辑
public MsgResult pay(String uid,double amount){
if(queryBalance(uid)<amount){
return new MsgResult(500,"支付失败","余额不足");
}else{
return new MsgResult(200,"支付成功","支付金额"+amount);
}
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 16:11 2020-3-12
*/
public class Alipay extends Paymet{
@Override
public String getKeyName() {
return "支付宝";
}
@Override
protected double queryBalance(String uid) {
return 250;
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 16:16 2020-3-12
*/
public class JDpay extends Paymet {
@Override
public String getKeyName() {
return "京东白条";
}
@Override
protected double queryBalance(String uid) {
return 500;
}
}
/**
* @Author Darker
* @Descrption
* @Date : Created in 16:18 2020-3-12
*/
public class WechatPay extends Paymet {
@Override
public String getKeyName() {
return "微信支付";
}
@Override
protected double queryBalance(String uid) {
return 700;
}
}
/**
* @Author Darker
* @Descrption 订单
* @Date : Created in 15:45 2020-3-12
*/
@Data
@AllArgsConstructor
public class Order {
private String uid;
private String orderId;
private Double amount;
public MsgResult pay(String payKey){
Paymet paymet = PayStrategy.getInstance(payKey);
MsgResult result = paymet.pay(orderId, amount);
return result;
}
}
/**
* @Author Darker
* @Descrption 返回包装类
* @Date : Created in 16:07 2020-3-12
*/
@Data
@AllArgsConstructor
public class MsgResult {
private int code;
private Object data;
private String msg;
@Override
public String toString() {
return "支付状态:[" + code +"]," + msg +",交易详情"+ data;
}
}
/**
* @Author Darker
* @Descrption 支付策略
* @Date : Created in 16:25 2020-3-12
*/
public class PayStrategy {
public static final String ALI_PAY = "Alipay";
public static final String JD_PAY = "Jdpay";
public static final String WECHAT_PAY = "Wechatpay";
public static final String DEFAULT_PAY = ALI_PAY;
private static Map<String, Paymet> payStrategy =new HashMap<>();
static {
payStrategy.put(ALI_PAY,new Alipay());
payStrategy.put(JD_PAY,new JDpay());
payStrategy.put(WECHAT_PAY,new WechatPay());
}
public static Paymet getInstance(String payKey){
Paymet paymet = payStrategy.get(payKey) ;
return paymet == null ? payStrategy.get(DEFAULT_PAY):paymet;
}
}
/**
* @Author Darker
* @Descrption 测试
* @Date : Created in 16:21 2020-3-12
*/
public class payStrategyTest {
public static void main(String[] args) {
String payKey = "Jdpay";
String payKey2 = "Wechatpay";
//用户提交了一个订单
Order order = new Order("1", "2020031200000000", 570.00);
//用户选择支付方式,京东白条
MsgResult result = order.pay(payKey);
System.out.println(result);
//用户选择支付方式,微信支付
MsgResult result2 = order.pay(payKey2);
System.out.println(result2);
}
}
来看看,这是不是就是我们实际上支付使用的方式呢。
好,最后,我们再来说说jdk在哪里用了策略模式呢,答案就是Comparator,比较器,我们经常用比较器来排序,这个比较器里面有很多的算法,我们可以随意选择自己喜欢的策略来排序,非常经典。
同样,在spring中也用到了策略模式,在类的初始化的时候它会选择是用jdk的方式还是cglib的方式。
总结:
优点:
1.策略模式符合开闭原则
2.避免使用多重条件转移语句,如if...else.....
3.使用策略模式可以提高算法的保密性和安全性
缺点:
1.客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
2.代码中会产生非常多的策略类,增加维护难度。