话不多说。先讲一下我理解的策略模式。

策略模式

将类的行为抽象出来,使其可以在运行时改变。用于改善代码中过多的if else也可以用于区分业务。

spring下使用策略模式,通过spring的IOC自动注入,来匹配我们要实现的策略。

我模拟订单创建做了一个demo,结合了策略模式和模板模式,下面上代码:

public interface CreateStrategy<T> {

    /**
     * 创建
     */
    boolean create(T order);
}

然后我们写一个他的实现模板

@Slf4j
public abstract class AbstractCreateStrategy<T> implements CreateStrategy<T> {

    @Override
    public boolean create(T order) {
        log.info("创建");
        return checkParamter(order) && save(order) && saveAfter(order);
    }

    protected abstract boolean checkParamter(T dto);

    protected abstract boolean save(T dto);

    protected abstract boolean saveAfter(T dto);

    protected abstract boolean sendOther();

}

接下来创建行为的实现,都继承这个抽象类来实现。

@Slf4j
@Service("b2bOutCreate")
public class B2bOutCreateStrategy extends AbstractCreateStrategy<OutOrderCreate> implements CreateStrategy<OutOrderCreate> {

    @Override
    protected boolean checkParamter(OutOrderCreate dto) {
        log.info("b2bOutCreate--校验数据");
        return true;
    }

    @Override
    protected boolean save(OutOrderCreate dto) {
        log.info("b2bOutCreate--保存主表");
        return true;
    }

    @Override
    protected boolean saveAfter(OutOrderCreate dto) {
        log.info("b2bOutCreate--保存后处理");
        return true;
    }

    @Override
    protected boolean sendOther() {
        return false;
    }
}
@Slf4j
@Service("b2cInCreate")
public class B2cInCreateStrategy extends AbstractCreateStrategy<InOrderCreate> implements CreateStrategy<InOrderCreate> {

    @Override
    protected boolean checkParamter(InOrderCreate dto) {
        log.info("b2cInCreate--校验数据");
        return true;
    }

    @Override
    protected boolean save(InOrderCreate dto) {
        log.info("b2cInCreate--主表保存");
        return true;
    }

    @Override
    protected boolean saveAfter(InOrderCreate dto) {
        log.info("b2cInCreate--主表保存后处理");
        return true;
    }

    @Override
    protected boolean sendOther() {
        return false;
    }
}

策略接口和实现我们定义好了。接下来需要定义一个策略转换器。来匹配我们controller请求的类型。

同样也使用了模板模板,先看一下抽象类

@Data
public abstract class AbstractStrategyContext<R> {

    @Autowired
    Map<String, R> map;

    /**
     * 根据type获取对应的策略实例
     * @param type 策略名称
     * @return 策略实例
     */
    R getStrategy(String type) {
        return Optional.ofNullable(getMap().get(type)).orElseThrow(() -> new RuntimeException("类型:" + type + "未定义"));
    }

}

这里注入一个map<String,R>,这里泛型R是我们后面子类中定义的具体行为实现。

@autowired自动注入时,会将我们的同一个接口的所有实现,放入map中,map的key是实现了该接口的@service的名字,value则是该接口的具体实现的单例实例。

然后我们定义一个方法,根据传入的type作为key值,获取对应的接口实现实例。 

这时候我们写一个创建相关的策略器。

@Component
public class CreateStrategyContext extends AbstractStrategyContext<CreateStrategy> {

    public <T> boolean create(T dto,String type) {
        return getStrategy(type).create(dto);
    }


}

这样我们的策略器就写好了。接下来完善controller

@RestController
@RequestMapping("/out")
public class OutOrderController {

    private final CreateStrategyContext createStrategyContext;


    @Autowired
    public OutOrderController(CreateStrategyContext createStrategyContext, OutStockStrategyContext outStockStrategyContext) {
        this.createStrategyContext = createStrategyContext;
        this.outStockStrategyContext = outStockStrategyContext;
    }

    @PostMapping("/create/{type}")
    public boolean outCreate(@RequestBody OutOrderCreate order, @PathVariable("type")String type) {
        return createStrategyContext.create(order,type);
    }



}

controller注入一个创建行为的策略器,我们这里为了方便将传入类型通过url的type传入。 

这样我们就实现了根据不同行为传入不同的实例进行匹配。省去了在controller根据类型多个if else来判断业务,再注入具体实现。 

其实代码实现还是挺简单的。主要是在思考一个问题。我们平时做一个需求,比如做一个客户的CRUD,我们会先定义一个customerService接口。然后定义sleect ,insert,update,delete方法。然后写一个类实现该接口,然后在controller注入该接口。

其实跟我们直接出入类也没有区别。因为注入一个具体的接口时,这个接口是不能有多个实现的。不然就要多用一个注解来标注具体使用哪个。那么当我们在写一个新的需求,供应商的CRUD。其实还是这4个方法。那我们又定义了一遍这个接口,只不过入参不一样而已。

而我们使用策略模式后,我们其实开发的思路就会进行转变,比如customer的CRUD,我们其实定义4个行为,然后根据不同的业务来实现该行为。这样就避免了会重复定义多个同样的接口的烦恼。并且当这个功能需要拓展时,如果我们改接口,那么所有的实现都要增加这个方法的实现。这其实违反了开闭原则,而策略模式下则可以定义一个新的行为接口并且对其进行实现。只进行扩展不对原有代码进行修改。这也算是优势吧,最近也在想,是不是之前的开发其实都是有问题的,这才是spring想让使用的开发方式呢。