需求描述
在一个交易系统中,由于历史原因,存在2类商户。一类属于自营商户,存储于A表(Id,商户号,名称)中,一类属于外部平台商户,存储于B表(Id,平台号,商户号,名称)中
目前需要筛查系统中所有的商户,同时再对这些数据做一些处理,筛查模式有3种:
- 1.所有的商户:包括 自营商户(A表)和 平台商户(B表)以及再做一些处理工作;
- 2.根据平台号:全部的自营商户(A表)或者 平台商户(B表)中指定的平台,以及再做一些处理工作;
(其中平台号为0,为自营商户) - 3.根据商户号:筛查所有的商户,包括 自营商户(A表)和 平台商户(B表),以及再做一些处理工作。
tip:(- 除以上3种外,未来可能的还会补充一些筛查方法,据说产品经理现在还没想好,毕竟是惹不起的人,随时加需求…what…·~)
以上需求不算太复杂,根据其描述,很快就能定义出大致的逻辑处理实现。
public void check(int type, String pId, String mId) {
if (type == 1) {
//1.所有的商户
List allA = tableAService.selectAll();//自营
doSomething(allA);
List allB = tableBService.selectAll();//平台
doSomething(allB);
} else if (type == 2) {
//2.根据平台
if ("0".equals(pId)) {
List allA = tableAService.selectAll();//自营
doSomething(allA);
} else {
List pidB = tableBService.selectPid(pId);//平台
doSomething(pidB);
}
} else if (type == 3) {
//3.根据商户号
List midA = tableAService.selectMid(mId);//自营
doSomething(midA);
List midB = tableBService.selectMid(mId);//平台
doSomething(midB);
}
// ...
}
我想说的是,功能实现是没有问题的,有没有更好的方法呢。比如万一哪天,产品经理说,我们需要第4种模式,同时根据平台和商户号查询呢?
我们可能就需要再新增一个if条件分支…
public void check(int type, String pId, String mId) {
if (type == 1) {
//1.所有的商户
List allA = tableAService.selectAll();
doSomething(allA);
List allB = tableBService.selectAll();
doSomething(allB);
} else if (type == 2) {
//2.根据平台
if ("0".equals(pId)) {
List allA = tableAService.selectAll();
doSomething(allA);
} else {
List pidB = tableBService.selectPid(pId);
doSomething(pidB);
}
} else if (type == 3) {
//3.根据商户号
List midA = tableAService.selectMid(mId);
doSomething(midA);
List midB = tableBService.selectMid(mId);
doSomething(midB);
} else if (type == 4){
//4.fuck xxxx
}
// ...
}
做过开发的朋友都会明白了,以上代码要维护的话,先要能够找到对应的代码在if分支块中,而且if的缩进代码块,也容易让人看花眼,万一没对齐就惨了.。
这里,我将介绍一种方法,使用策略模式改造if分支过多的方法
为了结合项目代码,使用Spring框架,实现类的管理,采用自定义注解与枚举类结合,简化代码。
首先定义我们的type枚举类
CheckTypeEnum.java
public enum CheckTypeEnum {
All(1, "所有商户"),
PID(2, "根据平台"),
MID(3, "根据商户号"),
;
private Integer type;
private String desc;
CheckTypeEnum(Integer type, String desc) {
this.type = type;
this.desc = desc;
}
//getter and setter ...
}
自定义注解:
CheckTypeAnno.java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CheckTypeAnno {
CheckTypeEnum value();
}
定义我们的策略接口
UserCheckStrategy.java
public interface UserCheckStrategy {
void check(int type, String pId, String mId);
}
第一种类型的检查方式实现类
UserCheckAll.java
@Service
@CheckTypeAnno(CheckTypeEnum.All)
public class UserCheckAll implements UserCheckStrategy {
@Resource
TableAService tableAService;
@Resource
TableBService tableBService;
@Override
public void check(int type, String pId, String mId) {
//1.所有的商户
List allA = tableAService.selectAll();
doSomething(allA);
List allB = tableBService.selectAll();
doSomething(allB);
}
private void doSomething(List list) {
// doSomething
}
}
第二种类型的检查方式实现类
UserCheckPid.java
@Service
@CheckTypeAnno(CheckTypeEnum.PID)
public class UserCheckPid implements UserCheckStrategy {
@Resource
TableAService tableAService;
@Resource
TableBService tableBService;
@Override
public void check(int type, String pId, String mId) {
//2.根据平台
if ("0".equals(pId)) {
List allA = tableAService.selectAll();
doSomething(allA);
} else {
List pidB = tableBService.selectPid(pId);
doSomething(pidB);
}
}
private void doSomething(List list) {
// doSomething
}
}
第三种类型的检查方式实现类
UserCheckPid.java
@Service
@CheckTypeAnno(CheckTypeEnum.MID)
public class UserCheckMid implements UserCheckStrategy {
@Resource
TableAService tableAService;
@Resource
TableBService tableBService;
@Override
public void check(int type, String pId, String mId) {
//3.根据商户号
List midA = tableAService.selectMid(mId);
doSomething(midA);
List midB = tableBService.selectMid(mId);
doSomething(midB);
}
private void doSomething(List list) {
// doSomething
}
}
策略上下文对象
UserCheckStrategyContext.java
@Service
public class UserCheckStrategyContext {
@Resource
List<UserCheckStrategy> userCheckStrategies;
void check(int type, String pId, String mId) {
for (UserCheckStrategy userCheckStrategy : userCheckStrategies) {
CheckTypeAnno checkTypeAnno = userCheckStrategy.getClass().getAnnotation(CheckTypeAnno.class);
if (checkTypeAnno != null && checkTypeAnno.value().getType() == type) {
userCheckStrategy.check(type,pId,mId);
return;
}
}
}
}
这里将userCheckStrategies的所有实现类交由Spring管理,通过匹配实现类上标注的注解,来决定使用具体哪种策略实现类进行商户数据筛查。
最终改造之后的客户端调用方法为:
UserService.java
...
@Resource
UserCheckStrategyContext userCheckStrategyContext;
/**
* 处理商户
*
* @param type 1.所有的商户 2.根据平台号 3.根据商户号
* @param pId 平台号
* @param mId 商户号
*/
public void check(int type, String pId, String mId) {
/*if (type == 1) {
//1.所有的商户
List allA = tableAService.selectAll();
doSomething(allA);
List allB = tableBService.selectAll();
doSomething(allB);
} else if (type == 2) {
//2.根据平台
if ("0".equals(pId)) {
List allA = tableAService.selectAll();
doSomething(allA);
} else {
List pidB = tableBService.selectPid(pId);
doSomething(pidB);
}
} else if (type == 3) {
//3.根据商户号
List midA = tableAService.selectMid(mId);
doSomething(midA);
List midB = tableBService.selectMid(mId);
doSomething(midB);
} else if (type == 4){
//4.fuck xxxx
}*/
// ...
// 使用策略模式后,方法调用为:
userCheckStrategyContext.check(type,pId,mId);
}
改造后,每种情况的检查,将是一个具体的枚举值以及对应的具体策略接口实现类。
需求变更
这个时候,出现了第四种情况,只需要对应的维护一个接口实现类以及枚举变量值了。
如下
CheckTypeEnum.java
public enum CheckTypeEnum {
All(1, "所有商户"),
PID(2, "根据平台"),
MID(3, "根据商户号"),
MORE(4, "更多"),
;
private Integer type;
private String desc;
CheckTypeEnum(Integer type, String desc) {
this.type = type;
this.desc = desc;
}
}
UserCheckMore.java
@Service
@CheckTypeAnno(CheckTypeEnum.MORE)
public class UserCheckMore implements UserCheckStrategy {
@Override
public void check(int type, String pId, String mId) {
//4.更多处理方式
// ...
}
}
其他原有的方式与结构不需要任何变化!
演示项目的文件结构:
总结
通过将控制分支与具体的处理逻辑分离,解决了if分支过多的问题。这里实际上是利用了Java接口的多态特性,实现了分支选择。
具体选择哪个分支,由上下文对象根据其实现类上标注的注解进行选择。
这样,即便出现了筛查模式类型type为:4,5,6,7…
再多分支,也能轻松应对!
面向对象的程序设计中,有一条开闭原则,它规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。
代码的修改是不可避免的,但是通过结构的改造,能够最小限度的减少其修改,也更方便维护。
欢迎看过的朋友底下留言~