行外人看不見的債務-技術債務 中的 WeekDay
如果比著筆者去做,會點樣寫?
筆者認為呢一個 WeekDay
Enum
寫法都仲可以接受到,因為佢真係淨係 WeekDay
,唔會再加上去。
若你細心啲諗,我寫 Test Case 要寫哂 7 個 Test case 去滿足 7 個 if
case,先可以達到 100% coverage,7 個 if
都係做緊同類嘅野,所以係有地方可以改善;一般測試 parse
呢類 Method 理應只有兩類 Test case - Valid/Invalid test case。
其實除左 Testability 比較差之外,該寫法並不 Scalable,例如加多一個 Type 叫 UNKNOWN
去處理啲唔係 WeekDay 嘅 Type ,難道我又要加多一個 if
?
原始的 Code
public enum WeekDay{
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
public static WeekDay parse(String str){
WeekDay weekDay = null;
if("monday".equalsIgnoreCase(str) || "1".equals(str)){
weekDay = WeekDay.MONDAY;
} else if("tuesday".equalsIgnoreCase(str) || "2".equals(str)){
weekDay = WeekDay.TUESDAY;
} else if("wednesday".equalsIgnoreCase(str) || "3".equals(str)){
weekDay = WeekDay.WEDNESDAY;
} else if("thursday".equalsIgnoreCase(str) || "4".equals(str)){
weekDay = WeekDay.THURSDAY;
} else if("friday".equalsIgnoreCase(str) || "5".equals(str)){
weekDay = WeekDay.FRIDAY;
} else if("saturday".equalsIgnoreCase(str) || "6".equals(str)){
weekDay = WeekDay.SATURDAY;
} else if("sunday".equalsIgnoreCase(str) || "7".equals(str)){
weekDay = WeekDay.SUNDAY;
}
return weekDay;
}
}
筆者的 Refactor
筆者答案一 (Scalable,理論上最快速,多用咗 Memory)
Concept: Application 行之前將整個 Enum Array 放入 Static HashMap 內,其後每次運行 RefactoredWeekDay.parse
為 O(1)
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public enum RefactoredWeekDay {
MONDAY(1),
TUESDAY(2),
WEDNESDAY(3)
THURSDAY(4),
FRIDAY(5),
SATURDAY(6),
SUNDAY(7);
Integer weekDayInt;
private static Map<String,RefactoredWeekDay> strMap = new HashMap<>();
private static Map<Integer,RefactoredWeekDay> intMap = new HashMap<>();
static {
strMap = Stream.of(RefactoredWeekDay.values())
.collect(Collectors.toMap(
RefactoredWeekDay::name,
(wd) -> wd
));
intMap = Stream.of(RefactoredWeekDay.values())
.collect(Collectors.toMap(
RefactoredWeekDay::getWeekDayInt,
(wd) -> wd
));
}
RefactoredWeekDay(Integer weekDayInt){
this.weekDayInt = weekDayInt;
}
public Integer getWeekDayInt(){
return this.weekDayInt;
}
public static Optional<RefactoredWeekDay> parse(String str){
Objects.requireNonNull(str);
if(str.matches("[0-9]+")){
return Optional.ofNullable(intMap.get(Integer.parseInt(str)));
}
return Optional.ofNullable(strMap.get(str.toUpperCase()));
}
}
筆者答案二 (慳 Memory 多用啲 CPU)
Concept: 每次都會 Iterate 整個 Enum Array 運行RefactoredWeekDay.parse
為 O(n)
import java.util.Optional;
import java.util.stream.Stream;
public enum RefactoredWeekDay {
MONDAY(1),
TUESDAY(2),
WEDNESDAY(3),
THURSDAY(4),
FRIDAY(5),
SATURDAY(6),
SUNDAY(7);
Integer weekDayInt;
RefactoredWeekDay(Integer weekDayInt){
this.weekDayInt = weekDayInt;
}
public Integer getWeekDayInt(){
return this.weekDayInt;
}
private boolean isEqual(String weekDayStr){
return this.name().equalsIgnoreCase(weekDayStr)
|| String.valueOf(this.getWeekDayInt()).equals(weekDayStr);
}
public static Optional<RefactoredWeekDay> parse(String str){
return Stream.of(RefactoredWeekDay.values())
.filter(wd -> wd.isEqual(str))
.findFirst();
}
}
總結
如果 Application 內經常需要運行 RefactoredWeekDay.parse
,答案一比較合適(利用 Memory 加 HashMap 加速)。相反,則為答案二比較適合。
但其實 JVM 有 Adaptive Optimizer,相信答案二的方法在實際環境不會比答案一相差得遠。實際環境邊個好啲,真係要跑過 Micro Benchmark 先至知!
歡迎各位將你嘅答案 Inbox 比筆者 :)
筆者會不斷更新此 Post,比大家睇下各個不同 Programmer 嘅風格~