我正在参与「启航方案」

最近这段时间,想给咱们共享一下规划形式的一些用法以及在项目中怎样运用。

规划形式软件规划中常见问题的典型处理方案。它们就像能根据需求进行调整的预制蓝图,可用于处理代码中反复呈现的规划问题。

今天就拿其间一个问题来分析,运用战略形式来处理问题,没有了解过战略形式或许长时间不必现已忘了战略形式的小伙伴先来简略了解一下战略形式吧。

什么是战略形式?

战略形式是一种行为型形式,它将目标和行为分开,将行为界说为 一个行为接口详细行为的完成。战略形式最大的特点是行为的变化,行为之间能够彼此替换。每个if判别都能够理解为便是一个战略。本形式使得算法可独立于运用它的用户而变化。

简略理解便是,针对不同的场景,运用不同的战略进行处理。

战略形式结构

如何优雅的使用策略模式

  • Strategy 接口界说了一个算法族,它们都完成了 behavior() 办法。

  • Context 是运用到该算法族的类,其间的 doSomething() 办法会调用 behavior(),setStrategy(Strategy) 办法能够动态地改变 strategy 目标,也便是说能动态地改变 Context 所运用的算法。

战略形式适用场景

  • 假如在一个体系里边有许多类,它们之间的区别仅在于它们 的行为,那么运用战略形式能够动态地让一个目标在许多行 为中挑选一种行为。
  • 一个体系需求动态地在几种算法中挑选一种。
  • 假如一个目标有许多的行为,假如不必恰当的形式,这些行 为就只好运用多重的条件挑选语句来完成。
  • 不期望客户端知道杂乱的、与算法相关的数据结构,在详细战略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

生活中比较常见的运用形式有:

  1. 电商网站付出办法,一般分为银联、微信、付出宝,能够选用战略形式。
  2. 电商网站活动办法,一般分为满减送、限时扣头、包邮活动,拼团等能够选用战略形式。

简略示例

场景:最近太热了,想要降降温,有什么办法呢

首先,界说一个降温战略的接口

public interface CoolingStrategy {
    /**
     * 处理办法
     */
    void handle();
}

界说3种降温战略;完成战略接口

public class IceCoolingStrategy implements CoolingStrategy {
    @Override
    public void handle() {
        System.out.println("运用冰块降温");
    }
}
public class FanCoolingStrategy implements CoolingStrategy {
    @Override
    public void handle() {
        System.out.println("运用风扇降温");
    }
}
public class AirConditionerCoolingStrategy implements CoolingStrategy {
    @Override
    public void handle() {
        System.out.println("运用空调降温");
    }
}

界说一个降温战略的上下文

public class CoolingStrategyContext {
    private final CoolingStrategy strategy;
    public CoolingStrategyContext(CoolingStrategy strategy) {
        this.strategy = strategy;
    }
    public void coolingHandle() {
        strategy.handle();
    }
} 

测验

public class Main {
    public static void main(String[] args) {
        CoolingStrategyContext context = new CoolingStrategyContext(new FanCoolingStrategy());
        context.coolingHandle();
        context = new CoolingStrategyContext(new AirConditionerCoolingStrategy());
        context.coolingHandle();
        context = new CoolingStrategyContext(new IceCoolingStrategy());
        context.coolingHandle();
    }
} 

运转成果:

运用风扇降温
运用空调降温 
运用冰块降温 

以上便是一个战略形式的简略完成

项目实战

场景

模拟在购买产品时分运用的各种类型优惠券(满减、直减、扣头、n元购)

这个场景几乎也是咱们的一个日常购物省钱途径,购买产品的时分都期望找一些优惠券,让购买的产品愈加实惠。并且到了大促的时分就会有更多的优惠券需求核算那些产品一起购买愈加优惠!

用一坨坨代码完成

/**
 * 优惠券扣头核算接口
 * <p>
 * 优惠券类型;
 * 1. 直减券
 * 2. 满减券
 * 3. 扣头券
 * 4. n元购
 */
public class CouponDiscountService {
    public double discountAmount(int type, double typeContent, double skuPrice, double typeExt) {
        // 1. 直减券
        if (1 == type) {
            return skuPrice - typeContent;
        }
        // 2. 满减券
        if (2 == type) {
            if (skuPrice < typeExt) return skuPrice;
            return skuPrice - typeContent;
        }
        // 3. 扣头券
        if (3 == type) {
            return skuPrice * typeContent;
        }
        // 4. n元购
        if (4 == type) {
            return typeContent;
        }
        return 0D;
    }
}
  • 以上是不同类型的优惠券核算扣头后的实践金额。
  • 入参包含;优惠券类型、优惠券金额、产品金额,因为有些优惠券是满多少削减多少,所以增加了typeExt类型。这也是办法的欠好扩展性问题。
  • 终究是整个的办法体中对优惠券抵扣金额的完成,最开端或许是一个最简略的优惠券,后面跟着产品功能的增加,不断的扩展if语句。实践的代码或许要比这个多许多

战略形式重构代码

如何优雅的使用策略模式

  • 整体的结构形式并不杂乱,主要表现的不同类型的优惠券在核算优惠券办法的不同核算战略。
  • 这里包含一个接口类(ICouponDiscount)以及四种优惠券类型的完成办法。
  • 终究供给了战略形式的上下控制类处理,整体的战略服务。

代码完成

优惠券接口

public interface ICouponDiscount<T> {
    /**
     * 优惠券金额核算
     * @param couponInfo 券扣头信息;直减、满减、扣头、N元购
     * @param skuPrice   sku金额
     * @return           优惠后金额
     */
    BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);
}
  • 界说了优惠券扣头接口,也增加了泛型用于不同类型的接口能够传递不同的类型参数。
  • 接口中包含产品金额以及出参回来终究扣头后的金额,这里在实践开发中会比现在的接口参数多一些,但中心逻辑是这些。

优惠券接口完成

满减

public class MJCouponDiscount implements ICouponDiscount<Map<String,String>>  {
    /**
     * 满减核算
     * 1. 判别满意x元后-n元,否则不减
     * 2. 最低付出金额1元
     */
    public BigDecimal discountAmount(Map<String,String> couponInfo, BigDecimal skuPrice) {
        String x = couponInfo.get("x");
        String o = couponInfo.get("n");
        // 小于产品金额条件的,直接回来产品原价
        if (skuPrice.compareTo(new BigDecimal(x)) < 0) return skuPrice;
        // 减去优惠金额判别
        BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(o));
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
        return discountAmount;
    }
}

直减

public class ZJCouponDiscount implements ICouponDiscount<Double>  {
    /**
     * 直减核算
     * 1. 运用产品价格减去优惠价格
     * 2. 最低付出金额1元
     */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
        BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
        return discountAmount;
    }
}

扣头

public class ZKCouponDiscount implements ICouponDiscount<Double> {
    /**
     * 扣头核算
     * 1. 运用产品价格乘以扣头比例,为终究付出金额
     * 2. 保存两位小数
     * 3. 最低付出金额1元
     */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
        BigDecimal discountAmount = skuPrice.multiply(new BigDecimal(couponInfo)).setScale(2, BigDecimal.ROUND_HALF_UP);
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
        return discountAmount;
    }
}

N元购

public class NYGCouponDiscount implements ICouponDiscount<Double> {
    /**
     * n元购购买
     * 1. 无论原价多少钱都固定金额购买
     */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
        return new BigDecimal(couponInfo);
    }
}
  • 以上是四种不同类型的优惠券核算扣头金额的战略办法,能够从代码中看到每一种优惠办法的优惠金额。

战略控制类

public class Context<T> {
    private ICouponDiscount<T> couponDiscount;
    public Context(ICouponDiscount<T> couponDiscount) {
        this.couponDiscount = couponDiscount;
    }
    public BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice) {
        return couponDiscount.discountAmount(couponInfo, skuPrice);
    }
}
  • 战略形式的控制类主要是外部能够传递不同的战略完成,在经过一致的办法执行优惠战略核算。
  • 另外这里也能够包装成map结构,让外部只需求对应的泛型类型即可运用相应的服务。

测验类

public class ApiTest {
    private Logger logger = LoggerFactory.getLogger(ApiTest.class);
    @Test
    public void test_zj() {
        // 直减;100-10,产品100元
        Context<Double> context = new Context<Double>(new ZJCouponDiscount());
        BigDecimal discountAmount = context.discountAmount(10D, new BigDecimal(100));
        logger.info("测验成果:直减优惠后金额 {}", discountAmount);
    }
    @Test
    public void test_mj() {
        // 满100减10,产品100元
        Context<Map<String,String>> context = new Context<Map<String,String>>(new MJCouponDiscount());
        Map<String,String> mapReq = new HashMap<String, String>();
        mapReq.put("x","100");
        mapReq.put("n","10");
        BigDecimal discountAmount = context.discountAmount(mapReq, new BigDecimal(100));
        logger.info("测验成果:满减优惠后金额 {}", discountAmount);
    }
    @Test
    public void test_zk() {
        // 扣头9折,产品100元
        Context<Double> context = new Context<Double>(new ZKCouponDiscount());
        BigDecimal discountAmount = context.discountAmount(0.9D, new BigDecimal(100));
        logger.info("测验成果:扣头9折后金额 {}", discountAmount);
    }
    @Test
    public void test_nyg() {
        // n元购;100-10,产品100元
        Context<Double> context = new Context<Double>(new NYGCouponDiscount());
        BigDecimal discountAmount = context.discountAmount(90D, new BigDecimal(100));
        logger.info("测验成果:n元购优惠后金额 {}", discountAmount);
    }
}
  • 以上四组测验别离验证了不同类型优惠券的优惠战略,测验成果是满意咱们的预期。
  • 这里四种优惠券终究都是在原价100元上扣头10元,终究付出90元

总结

经过战略规划形式的运用能够把咱们办法中的 if 语句优化掉,很多的 if 语句运用会让代码难以扩展,也欠好保护,一起在后期遇到各种问题也很难保护。在运用这样的规划形式后能够很好的满意隔离性与和扩展性,关于不断新增的需求也非常便利承接。

了解战略形式的长处和缺点,合理的运用战略形式,会让你的代码愈加的整齐优雅。