今天我们来了解下设计模式中比较常用的策略模式

什么是策略模式?

策略模式定义了一系列的算法,并将每一个算法封装起来,使每个算法可以相互替代,使算法本身和使用算法的客户端分割开来,相互独立。(摘自百度)

策略模式适用于什么场景?

一个大功能,它有许多不同类型的实现(策略类),具体根据客户端来决定采用特定的策略类。
比如下单、物流对接、网关的加签验签等。

以物流对接为例来比较简单工厂模式和策略模式。

具体业务为每个物流公司都有它们对应的code,加签方法,物流查询方法

首先我们定义好接口类

public interface Logistics {

    /**
     * 物流公司唯一标识
     * @return
     */
    String companyCode();

    /**
     * 加签
     * @param request
     */
    String sign(SignRequest request);

    /**
     * 查询物流信息
     */
    LogisticsInfoResponse queryLogisticsInfo(QueryLogisticsInfoRequest request);

}

对接顺风和圆通

@service("shunfengLogistics")
public class ShunfengLogistics implements Logistics {

    @Override
    public String companyCode() {
        return "shunfeng";
    }

    @Override
    public String sign(SignRequest request) {
        //do your biz
        
        return null;
    }

    @Override
    public LogisticsInfoResponse queryLogisticsInfo(QueryLogisticsInfoRequest request) {
        //do your biz
        
        return null;
    }
}

@service("yuantongLogistics")
public class YuantongLogistics implements Logistics {

    @Override
    public String companyCode() {
        return "yuantong";
    }

    @Override
    public String sign(SignRequest request) {
        //do your biz
        
        return null;
    }

    @Override
    public LogisticsInfoResponse queryLogisticsInfo(QueryLogisticsInfoRequest request) {
        //do your biz
        
        return null;
    }
}

简单工厂模式

public class LogisticsFactory {

    @Autowired
    private Logistics shunfengLogistics;

    @Autowired
    private Logistics yuantongLogistics;

    public Logistics getLogistics(LogisticsRequest request) {

        String company = request.getCompanyCode();

        if ("shunfeng".equals(company)) {
        
            return shunfengLogistics;
        }

        if ("yuantong".equals(company)) {
       
            return yuantongLogistics;
        }
        
        throw new BizException("物流公司类型错误");
    }
}

每当我们接入一家物流公司的时候都要使LogisticsFactory加一段if分支。这就违反了设计模式的开闭原则

策略模式结合Spring特性

其实不结合Spring特性的话,策略模式还是有上述问题存在。
我们先来准备下spring相关知识

1.实现InitializingBean接口。相信这个大家并不陌生。就是spring容器创建bean的时候帮你执行初始化的接口

2.实现ApplicationContextAware接口。通俗的讲,实现了这个接口的bean相当于是拿到了ApplicationContext,相当于整个spring容器的资源。

3.ApplicationContext接口的getBeansOfType方法。获取整个spring容器中某个类型的bean,返回的是map类型。key对应beanName, value对应bean。

@Component
public class LogisticsResolver implements InitializingBean, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private Map<String, Sign> logisticsHandlerMap = new HashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() {
        Map<String, Sign> beanMap = applicationContext.getBeansOfType(Logistics.class);
        for (String key : beanMap.keySet()) {
            this.signHandlerMap.put(beanMap.get(key).companyCode(), beanMap.get(key));
        }
    }

    public Logistics getHandler(String companyCode) {
        return logisticsHandlerMap.get(companyCode);
    }

}

通过spring容器去获取对应的所有实现类,并组装成我们想要的companyCode→bean的map。通过getHandler方法就能拿到对应的实现类。

但是现在我们每次加一个策略类的时候都交由Spring去管理,只管加就行了