设计模式 ---- 策略模式最佳实践

提到设计模式,我想大家最熟悉的莫过于单例模式了,很多人应该都能写出好几种单例模式的实现。工作中除了单例模式之外,还有一种比较常用的设计模式就是策略模式了。

正好前段時間的需求用到了策略模式,进过多次调整改进,自认为实现了一种比较优雅的策略模式,现在分享出来,希望可以帮助到你。

作为一篇完整的文章,我们还是有必要介绍下什么是策略模式,然后会通过一个完整案例,实现一个策略模式的具体编码。

什么是策略模式

策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。 ----- 百度百科

大白话说就是,一个接口有多个实现类,在具体调用方法的时候,依据所需选择一个具体实现类去调用。

策略模式的组成

策略模式的组成组要分为3个部分

  • 接口,定义或描述了一组相关行为(方法声明)
  • 实现类,对接口方法的具体实现(至少有2个不同实现),如果只有一个实现,也就没有了选择的必要,也就不需要策略模式
  • 策略上下文,包装了策略模式与具体实现之间的映射关系,给定一个规则(标识),通过策略上下文可以获取到具体的实现(对象)。

策略模式实现案例 下面就以一个商场促销的例子来实现一个策略模式。 首先定义接口

package com.info.examples.strategy;

import java.math.BigDecimal;

public interface PromoteSalesService {

    /**
     * 获取商品促销价
     *
     * @param originalPrice 原价
     * @return 商品的促销价
     */
    BigDecimal getPromotionPrice(BigDecimal originalPrice);
}


复制代码

这里给到两种促销方案,一个是满199减50,一种是满100打9折,199以上打85折。 首先我们定义一个促销方式的枚举,方便后期使用

package com.info.examples.strategy;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 支持的促销方式枚举
 */
@AllArgsConstructor
@Getter
public enum PromoteTypeEnum {

    // 打折
    DISCOUNT("打折"),

    // 满减
    FULL_REDUCTION("满减");

    private String desc;
}

复制代码

相应的,我们会在接口类PromoteSalesService.java添加一个方法用来获取促销的方式,于是PromoteSalesService.java的代码变成了如下样子

package com.info.examples.strategy;

import java.math.BigDecimal;

public interface PromoteSalesService {

    /**
     * 获取商品促销价
     *
     * @param originalPrice 原价
     * @return 商品的促销价
     */
    BigDecimal getPromotionPrice(BigDecimal originalPrice);

    /**
     * 获取促销方式
     *
     * @return 促销方式
     */
    PromoteTypeEnum getType();
}

复制代码

实现接口

package com.info.examples.strategy;

import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * 打折促销
 */
@Service
public class DiscountPromoteServiceImpl implements PromoteSalesService {

    // 满100打9折,199以上打85折
    @Override
    public BigDecimal getPromotionPrice(BigDecimal originalPrice) {
        if (new BigDecimal("100").compareTo(originalPrice) > 0) {
            return originalPrice;
        } else if (new BigDecimal("199").compareTo(originalPrice) >= 0) {
            return originalPrice.multiply(new BigDecimal("0.9"));
        }
        return originalPrice.multiply(new BigDecimal("0.85"));
    }

    @Override
    public PromoteTypeEnum getType() {
        return PromoteTypeEnum.DISCOUNT;
    }
}

复制代码
package com.info.examples.strategy;

import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * 满减促销
 */
@Service
public class FullReductionPromoteServiceImpl implements PromoteSalesService {

    // 满 199 减 50
    @Override
    public BigDecimal getPromotionPrice(BigDecimal originalPrice) {
        if (new BigDecimal("199").compareTo(originalPrice) > 0) {
            return originalPrice;
        }
        return originalPrice.subtract(new BigDecimal("50"));
    }

    @Override
    public PromoteTypeEnum getType() {
        return PromoteTypeEnum.FULL_REDUCTION;
    }
}

复制代码

接下来是本文的核心,实现策略模式的上下文。

package com.info.examples.strategy;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class PromoteStrategyFactory implements ApplicationContextAware {

    private Map<PromoteTypeEnum, PromoteSalesService> PROMOTE_MAP = new ConcurrentHashMap<>(1 << 2);

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        // 通过 ApplicationContext 获取到所有 PromoteSalesService 的实例对象
        final Map<String, PromoteSalesService> beans = context.getBeansOfType(PromoteSalesService.class);
        // 把促销类型和具体的实现类形成映射关系存储到map
        beans.values().forEach(bean -> PROMOTE_MAP.put(bean.getType(), bean));
    }

    /**
     * 根据促销类型获取对应的实现
     *
     * @param promoteType
     * @return
     */
    public PromoteSalesService getInstance(PromoteTypeEnum promoteType) {
        return PROMOTE_MAP.get(promoteType);
    }
}

复制代码

主体编写完毕,开始测试:

package com.info.examples.controller;

import com.info.examples.strategy.PromoteSalesService;
import com.info.examples.strategy.PromoteStrategyFactory;
import com.info.examples.strategy.PromoteTypeEnum;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.Random;

@RestController
public class PromoteController {

    private final PromoteStrategyFactory strategyFactory;

    public PromoteController(PromoteStrategyFactory strategyFactory) {
        this.strategyFactory = strategyFactory;
    }

    @GetMapping("/getPrice/{price:^[1-9]\\d*$|^(?>[1-9]\\d*\\.\\d{1,8})$}")
    public String getPromotePrice(@PathVariable String price) {
        PromoteTypeEnum promoteType = new Random().nextInt(100) > 50 ? PromoteTypeEnum.FULL_REDUCTION : PromoteTypeEnum.DISCOUNT;
        final PromoteSalesService promoteSalesService = strategyFactory.getInstance(promoteType);
        final BigDecimal promotionPrice = promoteSalesService.getPromotionPrice(new BigDecimal(price));
        return String.format("原价 %s 使用 %s 促销后价格为 %f", price, promoteSalesService.getType(), promotionPrice.doubleValue());
    }
}

复制代码

启动项目,打开浏览器输入http://localhost:8080/getPrice/200就可以进行测试啦。

好了今天的分享就到这里,希望对你有帮助。打完收工!

在这里插入图片描述

猜你喜欢

转载自juejin.im/post/7068091625888546829