话不多说。先讲一下我理解的策略模式。
将类的行为抽象出来,使其可以在运行时改变。用于改善代码中过多的if else也可以用于区分业务。
spring下使用策略模式,通过spring的IOC自动注入,来匹配我们要实现的策略。
我模拟订单创建做了一个demo,结合了策略模式和模板模式,下面上代码:
public interface CreateStrategy {
/**
* 创建
*/
boolean create(T order);
}
然后我们写一个他的实现模板
@Slf4j
public abstract class AbstractCreateStrategy implements CreateStrategy {
@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 implements CreateStrategy {
@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 implements CreateStrategy {
@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 {
@Autowired
Map map;
/**
* 根据type获取对应的策略实例
* @param type 策略名称
* @return 策略实例
*/
R getStrategy(String type) {
return Optional.ofNullable(getMap().get(type)).orElseThrow(() -> new RuntimeException("类型:" + type + "未定义"));
}
}
这里注入一个map
@autowired自动注入时,会将我们的同一个接口的所有实现,放入map中,map的key是实现了该接口的@service的名字,value则是该接口的具体实现的单例实例。
然后我们定义一个方法,根据传入的type作为key值,获取对应的接口实现实例。
这时候我们写一个创建相关的策略器。
@Component
public class CreateStrategyContext extends AbstractStrategyContext {
public 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想让使用的开发方式呢。个人的一些想法。欢迎探讨。