一,怎么选择
如果需要固定的执行流程,选模版
如果不需要固定的执行流程,只需要对一个方法做具体抽象,选策略
参考文章:
常用设计模式汇总,告诉你如何学习设计模式
二,常用写法
子类 extends absClass implements businiessInterface
absClass = absClass impements strategyInterface
样例与详细分析
public class ECCBMS195ServiceImpl extends AbstractBmsService implements ECCBMS195Service {
public abstract class AbstractBmsService implements BmsService {
一,策略接口
方法1:抽象类实现
方法2:子类实现,标识策略对象。后续工厂模式有用
public interface strategyInterface {
boolean execute(EccMessageReqVO eccMessageReqVO) throws Exception;
BmsServiceEnum getType();
}
二,抽象类
absClass
@Override
@Transactional
public boolean execute(EccMessageReqVO eccMessageReqVO) throws Exception {
this.process(eccMessageReqVO);
return true;
}
public abstract void process(EccMessageReqVO eccMessageReqVO) throws Exception;
三,业务接口 businiessInterface
public interface EccBms111Service {
void bms111Execute(ReqEccBms111VO reqEccBms110VO) throws Exception;
}
四,子类
1,实现业务接口逻辑businiessInterface
public void bms111Execute(ReqEccBms111VO reqEccBms111VO) throws Exception {}
2,实现抽象方法 process。且抽象接口中,调用具体业务接口
public void process(EccMessageReqVO eccMessageReqVO) throws Exception {
this.bms111Execute(reqEccBms111VO);
}
3,实现枚举接口,标识自身策略对象类型
@Override
public BmsServiceEnum getType() {
return BmsServiceEnum.ECC_BMS111;
}
5,工厂模式
初始化对象,提供获取具体对象接口
@Component
public class BmsServiceSelector implements InitializingBean {
private static final Map<BmsServiceEnum, BmsService> serviceMap = new HashMap<>();
@Resource
private List<BmsService> EccServices;
@Override
public void afterPropertiesSet() throws Exception {
for (BmsService service : EccServices) {
serviceMap.put(service.getType(), service);
}
}
public BmsService getService(BmsServiceEnum bmsServiceEnum) {
if (null == bmsServiceEnum){
throw new IllegalArgumentException("操作失败!");
}
return serviceMap.get(bmsServiceEnum);
}
}
6,调用方
1,首先调用者,不同业务场景有自己的唯一标识,比如MQ下发时,不同的场景,MQ tag不同
根据tag - > 获取枚举 -》根据枚举 -〉 获取具体对象 - 》 用具体对象调用具体逻辑
2,调用执行逻辑是策略接口中方法,execute,这个接口有抽象类实现(非子类实现)
public interface BmsService {
boolean execute(EccMessageReqVO eccMessageReqVO) throws Exception;
BmsServiceEnum getType();
}
BmsServiceEnum bmsServiceEnum = BmsServiceEnum.fromValue(vo.getTags());
BmsService bmsService = bmsServiceSelector.getService(bmsServiceEnum);
if(Objects.isNull(bmsService)) return true;
vo.setEccServiceName(bmsServiceEnum.getName());
bmsService.execute(vo);
疑问:如果不在BmsService中定义,删除,直接在抽象类中写个,普通的方法execute(即模版方法),有问题吗?
你看下有问题吗,报错了。调用者获取的是策略接口对象BmsService,是这个接口调用的。
再次体现,针对接口编程,非实现类编程。
为什么有此一问呢?是不是想到了文章中这里,策略模式中定义Context,里面定义了抽象类,
private penguin _penguin;
然后直接根据抽象类对象,调用抽象类中抽象接口与普通接口。
违背了设计原则:依赖接口,非依赖具体类。
public class behaviorContext {
private penguin _penguin;
public behaviorContext(penguin newPenguin) {
_penguin = newPenguin;
}
public void setPenguin(penguin newPenguin) {
_penguin = newPenguin;
}
public void everyDay() {
_penguin.eating();
_penguin.sleeping();
_penguin.beating();
}
}
实际使用中,会这样用吗,依赖抽象类。见过如下
场景:不同业务场景,导入excel,读取excel数据,并返回不同场景的对象(用通配符T)
public class BatchVehicleInfoController {
private final ExcelUploadDataService<VehicleCoreExcel> vehicleCoreDataExcelService;
}
public abstract class ExcelUploadDataService<T> {
/**
* excel 读取含表头
*
* @param excelInputStream
* @return
*/
public ExcelReadResult<T> readWithHead(final InputStream excelInputStream, final Class<T> clazz) {}
}
三,这一波下来用了什么设计模式
哪一波?上文【常用写法】
子类 extends absClass implements businiessInterface
absClass = absClass impements strategyInterface
模版
抽象类中定义了模版方法execute(只有一个行为process),模版方法中,调用了抽象接口 process。
抽象类 + 模版方法 + 抽象接口(子类实现),根据这三点可以理解为模版模式
public boolean execute(EccMessageReqVO eccMessageReqVO) throws Exception { this.process(eccMessageReqVO); return true; }
策略
抽象类 + 抽象接口,可以理解为策略模式。这也是策略的一般格式。
二者异同
好像与模版模式,一样,那最大的不同是什么
我认为是调用者,获取对象的方式不同
模版模式,每一个场景对象直接new的。参考这篇文档
常用设计模式汇总,告诉你如何学习设计模式
如下
public class test {
public static void main(String[] args) {
System.out.println("littlePenguin:");
littlePenguin penguin1 = new littlePenguin();
penguin1.everyDay();
System.out.println("middlePenguin:");
middlePenguin penguin2 = new middlePenguin();
penguin2.everyDay();
System.out.println("bigPenguin:");
bigPenguin penguin3 = new bigPenguin();
penguin3.everyDay();
}
}
模版模式,优化了调用者对象的创建方式
文章描述如下
这里就是策略模式的重点,我们再看一下策略模式的定义“我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的context对象”,那么该contex对象如下:
public class behaviorContext {
private penguin _penguin;
public behaviorContext(penguin newPenguin) {
_penguin = newPenguin;
}
public void setPenguin(penguin newPenguin) {
_penguin = newPenguin;
}
public void everyDay() {
_penguin.eating();
_penguin.sleeping();
_penguin.beating();
}
}
最后看调用方式:
public class test {
public static void main(String[] args) {
behaviorContext behavior = new behaviorContext(new littlePenguin());
behavior.everyDay();
behavior.setPenguin(new middlePenguin());
behavior.everyDay();
behavior.setPenguin(new bigPenguin());
behavior.everyDay();
}
}
有何感想: 上面强调了两个对象
策略的对象 + context对象
你看最后调用的时候,还是new了,对应和模版模式相同。
只是包了一层,方法的真正的调用者不同
模版:new的具体对象直接调用
littlePenguin penguin1 = new littlePenguin();
penguin1.everyDay();
策略:抽象对象调用
把调用者对象包了一下,且这个对象是一个抽象的
private penguin _penguin;
调用者还是对象,是不过这个对象不是new的具体对象,是一个抽象对象
这也体现了设计原则:针对接口编程,不要针对实现编程
public void everyDay() {
_penguin.eating();
_penguin.sleeping();
_penguin.beating();
}
那context对象的作用是什么?
再看下概念:一个行为随着策略对象改变而改变的context对象
总体来看,还是创建对象,并封装了一个调用具体逻辑的方法
public void everyDay() {
_penguin.eating();
_penguin.sleeping();
_penguin.beating();
}
但是我觉得封装方法不是重点,封装的方法,可以看作就一个抽象方法 _penguin.beating();
它的作用还是,提供了一个创建对象的入口。
一句话,它做的工厂模式的事
工厂
上面第三点,完全体现了,工厂模式
四,总结
现在回头看,模版与策略主要区别
1,模版有一套固定行为,策略无
2,策略封装了,对象的创建与获取。像是一个不那么完整的工厂模式(对比上面第5点)