遇到的問題:一張記錄表,記錄了10個業務的字段,一個入參type說明了要修改哪個字段,最初是通過switch(type)case…來做的
但是涉及這樣子的判斷多了,每次都要不斷的switch,並且case裏面不同方法有不同的處理,一個公共的switch並不能夠滿足
又不能在每一個方法中都寫一個10個case的switch,代碼太過臃腫了,因而產生了今天的記錄,當然更加臃腫的if else更加能夠簡化啦
利用策略+工廠+反射,簡化臃腫的switch
一、常量映射類
- 現在定義一個常量類,表示了type和方法的映射
public enum LeaveTypeEnum {
CASUAL_LEAVE("事假","setCasualLeave","setClBackup","getCasualLeave","getClBackup","setCasual","getCasual"),
SICK_LEAVE("帶薪病假","setSickLeave","setSlBackup","getSickLeave","getSlBackup","setSick","getSick"),
SICK_LEAVE_NORMAL("普通病假","setSickLeaveNormal","setSlnBackup","getSickLeaveNormal","getSlnBackup", "setSickNormal", "getSickNormal"),
MARRIAGE_LEAVE("婚假", "setMarriageLeave", "setMlBackup", "getMarriageLeave", "getMlBackup", "setMarriage", "getMarriage"),
FUNERAL_LEAVE("喪假", "setFuneralLeave", "setFlBackup", "getFuneralLeave", "getFlBackup", "setFuneral", "getFuneral"),
MATERNITY_LEAVE("產假","setMaternityLeave","setMnlBackup","getMaternityLeave","getMnlBackup","setMaternity","getMaternity"),
MATERNITY_LEAVE_1("產假-難產假", "setMaternityLeave", "setMnlBackup", "getMaternityLeave", "getMnlBackup", "", ""),
MATERNITY_LEAVE_2("產假-多胞胎假","setMaternityLeave","setMnlBackup","getMaternityLeave","getMnlBackup","",""),
MATERNITY_LEAVE_3("產假-哺乳假", "setMaternityLeave", "setMnlBackup", "getMaternityLeave","getMnlBackup","",""),
MATERNITY_LEAVE_4("產假-產檢假", "setMaternityLeave", "setMnlBackup", "getMaternityLeave", "getMnlBackup", "setMaterPaternity","getMaterPaternity"),
MATERNITY_LEAVE_5("產假-流產假", "setMaternityLeave", "setMnlBackup", "getMaternityLeave", "getMnlBackup", "", ""),
PATERNITY_LEAVE("陪產假","setPaternityLeave","setPnlBackup","getPaternityLeave","getPnlBackup","setMaterPaternity","getMaterPaternity"),
ANNUAL_LEAVE("年假", "setAnnualLeave", "setAlBackup", "getAnnualLeave", "getAlBackup", "setAnnual", "getAnnual");
private String name;
private String setMethod;
private String setSubMethod;
private String getMethod;
private String getSubMethod;
private String setCalMethod;
private String getCalMethod;
LeaveTypeEnum(String name,String setMethod,String setSubMethod,String getMethod,String getSubMethod,String setCalMethod,String getCalMethod) {
this.name = name;
this.setMethod = setMethod;
this.setSubMethod = setSubMethod;
this.getMethod = getMethod;
this.getSubMethod = getSubMethod;
this.setCalMethod = setCalMethod;
this.getCalMethod = getCalMethod;
}
public String getName() {
return name;
}
public static String getMethodByNameForSet(String name) {
for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
if (leaveType.name.equals(name)) {
return leaveType.setMethod;
}
}
return null;
}
public static String getSubMethodByNameForSet(String name) {
for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
if (leaveType.name.equals(name)) {
return leaveType.setSubMethod;
}
}
return null;
}
public static String getMethodByNameForGet(String name) {
for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
if (leaveType.name.equals(name)) {
return leaveType.getMethod;
}
}
return null;
}
public static String getSubMethodByNameForGet(String name) {
for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
if (leaveType.name.equals(name)) {
return leaveType.getSubMethod;
}
}
return null;
}
public static String getMethodByNameCalForSet(String name) {
for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
if (leaveType.name.equals(name)) {
return leaveType.setCalMethod;
}
}
return null;
}
public static String getMethodByNameCalForGet(String name) {
for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
if (leaveType.name.equals(name)) {
return leaveType.getCalMethod;
}
}
return null;
}
}
- 以上一個映射類和反射就能夠解決問題,但是爲了更簡化並且利用上一些設計模式,就嘗試了策略+工廠
二、 接口
public interface InvokeStrategy {
Double getByColumn(String type,String leaveType,String methodName,Object obj);
三、接口實現類
@Slf4j
public class UserVacationInvokeHandler implements InvokeStrategy {
@Override
public Double getByColumn(String type,String leaveType,String methodName, Object obj) {
UserVacation userVacation = (UserVacation) obj;
try {
Method method = UserVacation.class.getDeclaredMethod(methodName);
Invokable<UserVacation, Object> invokable =
(Invokable<UserVacation, Object>) Invokable.from(method);
Double invoke = (Double) invokable.invoke(userVacation);
return invoke;
}catch (Exception e ){
log.error("方法調用失敗,type:{},method:{}",type,methodName);
}
return null;
}
@Override
public void setByColumn(String type, String leaveType,String methodName,Double param, Object obj) {
UserVacation userVacation = (UserVacation) obj;
try {
Method method = UserVacation.class.getDeclaredMethod(methodName,Double.class);
Invokable<UserVacation, Object> invokable =
(Invokable<UserVacation, Object>) Invokable.from(method);
invokable.invoke(userVacation,param);
}catch (Exception e ){
log.error("方法調用失敗,type:{},method:{}",type,methodName);
}
}
}
四、策略工廠
public class InvokeStrategyFactory {
private static InvokeStrategyFactory factory = new InvokeStrategyFactory();
private InvokeStrategyFactory(){
}
private static Map<String,InvokeStrategy> strategyMap = new HashMap<>();
static{
strategyMap.put("UserVacation", new UserVacationInvokeHandler());
strategyMap.put("UserVacationCal", new UserVacationCalInvokeHandler());
}
public InvokeStrategy creator(String type){
return strategyMap.get(type);
}
public static InvokeStrategyFactory getInstance(){
return factory;
}
}
五、策略上下文
public class InvokeStrategyContext {
private InvokeStrategy strategy;
public Double getByColumn(String type, String leaveType, String methodName,Object obj) {
strategy = InvokeStrategyFactory.getInstance().creator(type);
return strategy.getByColumn(type, leaveType, methodName,obj);
}
public void setByColumn(String type, String leaveType,String methodName, Double param, Object obj) {
strategy = InvokeStrategyFactory.getInstance().creator(type);
strategy.setByColumn(type, leaveType,methodName, param, obj);
}
public InvokeStrategy getStrategy() {
return strategy;
}
public void setStrategy(InvokeStrategy strategy) {
this.strategy = strategy;
}
}
六、使用
InvokeStrategyContext context = new InvokeStrategyContext();
Lock lock = new ReentrantLock();
lock.lock();
try {
//只需這兩行,就替換掉了一大堆的switch,並且能夠輕鬆的進行中間操作,不再臃腫
Double result = context.getByColumn("UserVacation", dto.getType(), methodOld, userVacation);
context.setByColumn("UserVacation", dto.getLeaveType(), setMethod, result, userVacation);
} catch (Exception e) {
lock.unlock();
log.error("更新uservation出現異常");
} finally {
lock.unlock();
}
七、互聯網上也不乏有一些方法
- 傳統反射
/***
*定義每種類型所對應的方法
*/
public class ReflectTest {
public void methodOne() {
System.out.println("one");
}
public void methodTwo() {
System.out.println("two");
}
public void methodThree() {
System.out.println("three");
}
public void methodFour() {
System.out.println("four");
}
}
/***
*
* 通過反射,動態調用方法。採用了Guava的工具類。
* */
@Test
public void testReflect() throws Exception {
//首字母大寫,根據類型拼接方法
String methodName = "method" + LOWER_CAMEL.to(UPPER_CAMEL, input);
Method method = ReflectTest.class.getDeclaredMethod(methodName);
Invokable<ReflectTest, Object> invokable =
(Invokable<ReflectTest, Object>) Invokable.from(method);
invokable.invoke(new ReflectTest());
}
- jdk8 lambda表達式
public void testJava8() {
Map<String, Consumer<ReflectTest>> functionMap = Maps.newHashMap();
functionMap.put("one", ReflectTest::methodOne);
functionMap.put("two", ReflectTest::methodTwo);
functionMap.put("three", ReflectTest::methodThree);
functionMap.put("four", ReflectTest::methodThree);
functionMap.get(input).accept(new ReflectTest());
}
- 枚舉
public enum EnumTest {
ONE("one") {
@Override
public void apply() {
System.out.println("one");
}
},
TWO("two") {
@Override
public void apply() {
System.out.println("two");
}
}, THREE("three") {
@Override
public void apply() {
System.out.println("three");
}
}, FOUR("four") {
@Override
public void apply() {
System.out.println("four");
}
};
public abstract void apply();
private String type;
EnumTest(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
// 枚舉測試
@Test
public void testEnum() {
EnumTest.valueOf(input.toUpperCase()).apply();
}
- 我上面的方法其實結合了枚舉和傳統的反射,這邊還看到了一個jdk8的實現,想必大家一定很好奇
八、jdk8的改造
- 以上利用consumer執行了一個method,不帶有返回值,我們執行set的方法的時候可以效法consumer,我們如果是get 怎麼辦呢?
- 我們在使用Lambda表達式的時候,都會用到foreach和map兩種算子,foreach接納參數consumer,不帶有返回值,map接納參數function,可以有映射的值,這就引導我們意識到了function
//無返回參數
Map<String, Consumer<ReflectTest>> functionMap = Maps.newHashMap();
functionMap.put("one", ReflectTest::methodOne);
functionMap.put("two", ReflectTest::methodTwo);
functionMap.put("three", ReflectTest::methodThree);
functionMap.put("four", ReflectTest::methodThree);
functionMap.get(input).accept(new ReflectTest());
//帶返回參數
Map<String, Function<ReflectTest,String>> map = Maps.newHashMap();
map.put("one", ReflectTest::methodOne);
map.put("two", ReflectTest::methodTwo);
map.put("three", ReflectTest::methodThree);
map.put("four", ReflectTest::methodThree);
String apply = map.get("one").apply(new ReflectTest());
System.out.println(apply);
//經測試,能夠滿足需求
- 那麼枚舉加傳統反射也可以通過以上lambda表達式的方式改造啦,一個全局的靜態的Map,接納了關於set和get的定義,在使用的時候調用即可