设计模式学习——策略模式

策略模式的定义

策略模式是指定义了算法策略,分别封装起来,让它们之间可以互相替换,让算法的变化不会影响到使用算法的用户。

可以避免多重分支的if else 和 switch 语句。

策略模式的适用场景

1.假如系统中有很多类,而它们的区别仅在于它们的行为不同

2.一个系统需要动态地在几种算法中选择一种

3.需要屏蔽算法规则

示例1

电商经常会在节日做活动,需要根据各种节日选择不同的优惠策略:

/**
 * 优惠策略
 */
public interface IPromotionStrategy {

    /** 做优惠 */
    void doPromotion();

}
/**
 * @Description: 无优惠策略
 */
public class EmptyStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("无活动优惠");
    }
}
/**
 * @Description: 优惠券优惠
 */
public class CouponStrategy implements IPromotionStrategy {

    @Override
    public void doPromotion() {
        System.out.println("领取优惠券,根据优惠券进行抵扣");
    }
}
/**
 * @Description: 返现优惠
 */
public class CashBackStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("返现,直接返现到支付宝余额");
    }
}
/**
 * @Description: 团购优惠
 */
public class GroupBuyStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("拼团成功,进行打折");
    }
}
/**
 * @Description: 优惠活动
 */
public class PromotionActivity {

    private IPromotionStrategy promotionStrategy;

    public PromotionActivity(IPromotionStrategy promotionStrategy) {
        this.promotionStrategy = promotionStrategy;
    }

    //执行活动
    public void execute(){
        promotionStrategy.doPromotion();
    }
}
public class PromotionActivityTest {

    public static void main(String[] args) {
        //六一八活动-优惠券策略
        PromotionActivity activity618 = new PromotionActivity(new CouponStrategy());
        //双十一活动-返现策略
        PromotionActivity activity1111 = new PromotionActivity(new CashBackStrategy());

        activity618.execute();
        activity1111.execute();
    }

}

当然,实际开发中并不会像上面一样写定优惠策略,而是动态地根据活动选择优惠策略。

进行优化:

    public static void main(String[] args) {
        PromotionActivity activity=null;
        
        //假如现在的活动是优惠券优惠的活动
        String activityKey = "Coupon";

        if (StringUtils.equals(activityKey,"Coupon")){
            activity=new PromotionActivity(new CouponStrategy());
        }else if (StringUtils.equals(activityKey,"CashBack")){
            activity=new PromotionActivity(new CashBackStrategy());
        }
        //......
        activity.execute();
    }
现在倒是动态分配了,但是每多一种新的优惠策略,都需要去这里的业务逻辑加if-else,还得将整套业务逻辑重新测试。程序猿又要加班了。
再次优化:
/**
 * @Description: 结合策略模式和注册式单例,将业务代码中的多个if-else判断进行优化
 */
public class PromotionStrategyFactory {

    //定义一个Map,在工厂类内部使用静态代码块在类加载时就对map进行初始化 -> 注册式单例的特征
    private static Map<String, IPromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<String, IPromotionStrategy>();

    static {
        PROMOTION_STRATEGY_MAP.put(PromotionKey.COUPON, new CouponStrategy());
        PROMOTION_STRATEGY_MAP.put(PromotionKey.CASH_BACK, new CashBackStrategy());
        PROMOTION_STRATEGY_MAP.put(PromotionKey.GROUP_BUY, new GroupBuyStrategy());
    }

    //构造方法私有化并提供公共的静态方法访问 -> 单例的特征
    private PromotionStrategyFactory() {
    }
    //根据优惠标识获得优惠策略
    public static IPromotionStrategy getPromotionStrategy(String promotionKey) {
        IPromotionStrategy strategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
        return strategy == null ? NON_PROMOTION : strategy;
    }

    //优惠标识Key
    private interface PromotionKey {
        String COUPON = "COUPON";
        String CASH_BACK = "CASH_BACK";
        String GROUP_BUY = "GROUP_BUY";
    }

    //常量-无优惠策略
    private static final IPromotionStrategy NON_PROMOTION = new EmptyStrategy();

}
public class PromotionActivityTest {
    public static void main(String[] args) {
        String promotionKey = "COUPON";
        PromotionActivity activity = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey));
        activity.execute();
    }
}

可以发现,再次优化后,每次增加新的优惠策略只需要增加注册式单例的私有Map,在Map中添加响应的优惠策略即可,不需要再去修改调用处的业务代码。

这种实现将原本繁杂的多重判断进行了简化,也使日后程序中业务逻辑的维护变得简单。

示例二

网购下单时我们需要选择不同的支付方式进行支付:

/**
 * @Description: 支付的抽象
 */
public abstract class Payment {

    //支付类型
    public abstract String getName();

    //查询余额
    protected abstract double queryBalance(String uid);

    //扣款支付
    public MsgResult pay(String uid,double amount){
        if (queryBalance(uid)<amount){
            return new MsgResult(500,"支付失败","余额不足");
        }
        return new MsgResult(200,"支付成功","支付金额"+amount);
    }
}
/**
 * @Description: 阿里支付
 */
public class AliPay extends Payment {
    @Override
    public String getName() {
        return "支付宝";
    }

    @Override
    protected double queryBalance(String uid) {
        return 900;
    }
}
/**
 * @Description: 京东支付
 */
public class JDPay extends Payment {
    @Override
    public String getName() {
        return "京东白条";
    }

    @Override
    protected double queryBalance(String uid) {
        return 500;
    }
}
/**
 * @Description: 银联支付
 */
public class UnionPay extends Payment {
    @Override
    public String getName() {
        return "银联支付";
    }

    @Override
    protected double queryBalance(String uid) {
        return 200;
    }
}
/**
 * @Description: 微信支付
 */
public class WechatPay extends Payment {
    @Override
    public String getName() {
        return "微信支付";
    }

    @Override
    protected double queryBalance(String uid) {
        return 700;
    }
}
/**
 * @Description: 支付状态
 */
public class PayState {
    private int code;
    private String msg;
    private Object data;

    public PayState(int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    @Override
    public String toString() {
        return "支付状态:["+code+"]," + msg + ", 交易详情: " + data;
    }
}
/**
 * @Description: 支付策略管理器
 * 注册式单例,提前注册好各式支付方式的实例
 */
public class PayStrategy {
    public static final String ALI_PAY = "ALI_PAY";
    public static final String JD_PAY = "JD_PAY";
    public static final String UNION_PAY = "UNION_PAY";
    public static final String WECHAT_PAY = "WECHAT_PAY";
    public static final String DEFAULT_PAY = "ALI_PAY";

    private static Map<String,Payment> payStrategy = new HashMap<>();

    static{
        payStrategy.put(ALI_PAY,new AliPay());
        payStrategy.put(JD_PAY,new JDPay());
        payStrategy.put(UNION_PAY,new UnionPay());
        payStrategy.put(WECHAT_PAY,new WechatPay());
    }

    public static Payment get(String payKey){
        if (!payStrategy.containsKey(payKey)){
            return payStrategy.get(DEFAULT_PAY);
        }
        return payStrategy.get(payKey);
    }
}
public class Order {
    private String uid;

    private String orderId;

    private double amount;

    public Order(String uid, String orderId, double amount) {
        this.uid = uid;
        this.orderId = orderId;
        this.amount = amount;
    }

    /**
     * 使用PayStrategy根据支付的类型key选择支付策略
     * 不需要再在业务中写多个条件判断了
     */
    public PayState pay(String payKey){
        Payment payment = PayStrategy.get(payKey);
        System.out.println("开始使用:" + payment.getName());
        System.out.println("本次支付金额:" + amount);
        return payment.pay(uid,amount);
    }
}

/**
 * @Auther: jesses
 * @Date: 2021/4/12
 * @Description: 下单测试
 */
public class PayTest {
    public static void main(String[] args) {
        Order order = new Order("1", "202104121042001", 322.55);
        //在使用时才确定支付方式的key
        PayState payState = order.pay(PayStrategy.ALI_PAY);
        System.out.println(payState);
    }
}

策略模式在JDK中的应用

如较常用的比较器Comparator接口,就是策略模式的抽象实现:

Comparator接口有许多的实现类,在使用时常常把Comparator作为参数传入作为排序策略,比如Arrays.sort():

以及TreeMap的构造方法:

策略模式在Spring中的应用:

比如Resource接口及其子类实现:

还有Spring的初始化也使用到了策略模式,如InstantiationStrategy类,可以发现它的一个实现类CglibSubclassingInstantiationStrategy 是继承 另一个实现类SimpleInstantiationStrategy的,

所以说策略之间还可以继承使用。

策略模式的优点

1,策略模式符合开闭原则

2,避免多重条件判断if-else语句,提高了程序的可维护性

3,策略模式对算法进行封装,提高了算法的保密性和安全性

策略模式的缺点

1,客户端必须知道所有的策略,才能自行决定使用哪一种策略

2,代码中会产生较多的策略类,增加了维护的难度

ps:设计模式就是这样,有优点也有缺点,过多使用设计模式也会增加程序的复杂度,一昧的套用设计模式并不可取,需要根据实际业务情况去应用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值