我们先来看看需要重构的功能是一个下拉选择框,可任意选择11部词典中的一部,访问地址:http://123.56.99.179/select/dictionary-select.jsp?dict=RANDOMHOUSE,在HTML中的效果如下图所示:

HTML代码如下:
1<select name="dict" id="dict" onchange="update();"> 2 3 <option value="ICIBA">iCIBA</option> 4 <option value="YOUDAO">Youdao</option> 5 <option value="COLLINS">Collins</option> 6 <option value="WEBSTER">Webster's</option> 7 <option value="OXFORD">Oxford</option> 8 <option value="CAMBRIDGE">Cambridge</option> 9 <option value="MACMILLAN">Macmillan</option> 10 <option value="HERITAGE">Heritage</option> 11 <option value="WIKTIONARY">Wiktionary</option> 12 <option value="WORDNET">WordNet</option> 13 <option value="RANDOMHOUSE" selected="selected">RandomHouse</option> 14 15</select>
我们接下来看看最初的JSP代码是如何实现这个功能的:
1<%@ page contentType="text/html;charset=UTF-8" language="java" %> 2<%@ page import="org.apdplat.superword.tools.WordLinker" %> 3<%@ page import="org.apdplat.superword.tools.WordLinker.Dictionary" %> 4 5 <select name="dict" id="dict" onchange="update();"> 6 <% 7 if (Dictionary.ICIBA==WordLinker.getValidDictionary(request.getParameter("dict"))) { 8 %> 9 <option value="ICIBA" selected="selected">爱词霸</option> 10 <option value="YOUDAO">有道</option> 11 <option value="COLLINS">柯林斯</option> 12 <option value="WEBSTER">韦氏</option> 13 <option value="OXFORD">牛津</option> 14 <option value="CAMBRIDGE">剑桥</option> 15 <option value="MACMILLAN">麦克米伦</option> 16 <option value="HERITAGE">美国传统</option> 17 <option value="WIKTIONARY">维基词典</option> 18 <option value="WORDNET">WordNet</option> 19 <option value="RANDOMHOUSE">RandomHouse</option> 20 <% 21 } else if (Dictionary.YOUDAO==WordLinker.getValidDictionary(request.getParameter("dict"))) { 22 %> 23 <option value="ICIBA">爱词霸</option> 24 <option value="YOUDAO" selected="selected">有道</option> 25 <option value="COLLINS">柯林斯</option> 26 <option value="WEBSTER">韦氏</option> 27 <option value="OXFORD">牛津</option> 28 <option value="CAMBRIDGE">剑桥</option> 29 <option value="MACMILLAN">麦克米伦</option> 30 <option value="HERITAGE">美国传统</option> 31 <option value="WIKTIONARY">维基词典</option> 32 <option value="WORDNET">WordNet</option> 33 <option value="RANDOMHOUSE">RandomHouse</option> 34 <% 35 } else if (Dictionary.COLLINS==WordLinker.getValidDictionary(request.getParameter("dict"))) { 36 %> 37 <option value="ICIBA">爱词霸</option> 38 <option value="YOUDAO">有道</option> 39 <option value="COLLINS" selected="selected">柯林斯</option> 40 <option value="WEBSTER">韦氏</option> 41 <option value="OXFORD">牛津</option> 42 <option value="CAMBRIDGE">剑桥</option> 43 <option value="MACMILLAN">麦克米伦</option> 44 <option value="HERITAGE">美国传统</option> 45 <option value="WIKTIONARY">维基词典</option> 46 <option value="WORDNET">WordNet</option> 47 <option value="RANDOMHOUSE">RandomHouse</option> 48 <% 49 } else if (Dictionary.WEBSTER==WordLinker.getValidDictionary(request.getParameter("dict"))) { 50 %> 51 <option value="ICIBA">爱词霸</option> 52 <option value="YOUDAO">有道</option> 53 <option value="COLLINS">柯林斯</option> 54 <option value="WEBSTER" selected="selected">韦氏</option> 55 <option value="OXFORD">牛津</option> 56 <option value="CAMBRIDGE">剑桥</option> 57 <option value="MACMILLAN">麦克米伦</option> 58 <option value="HERITAGE">美国传统</option> 59 <option value="WIKTIONARY">维基词典</option> 60 <option value="WORDNET">WordNet</option> 61 <option value="RANDOMHOUSE">RandomHouse</option> 62 <% 63 } else if (Dictionary.OXFORD==WordLinker.getValidDictionary(request.getParameter("dict"))) { 64 %> 65 <option value="ICIBA">爱词霸</option> 66 <option value="YOUDAO">有道</option> 67 <option value="COLLINS">柯林斯</option> 68 <option value="WEBSTER">韦氏</option> 69 <option value="OXFORD" selected="selected">牛津</option> 70 <option value="CAMBRIDGE">剑桥</option> 71 <option value="MACMILLAN">麦克米伦</option> 72 <option value="HERITAGE">美国传统</option> 73 <option value="WIKTIONARY">维基词典</option> 74 <option value="WORDNET">WordNet</option> 75 <option value="RANDOMHOUSE">RandomHouse</option> 76 <% 77 } else if (Dictionary.CAMBRIDGE==WordLinker.getValidDictionary(request.getParameter("dict"))) { 78 %> 79 <option value="ICIBA">爱词霸</option> 80 <option value="YOUDAO">有道</option> 81 <option value="COLLINS">柯林斯</option> 82 <option value="WEBSTER">韦氏</option> 83 <option value="OXFORD">牛津</option> 84 <option value="CAMBRIDGE" selected="selected">剑桥</option> 85 <option value="MACMILLAN">麦克米伦</option> 86 <option value="HERITAGE">美国传统</option> 87 <option value="WIKTIONARY">维基词典</option> 88 <option value="WORDNET">WordNet</option> 89 <option value="RANDOMHOUSE">RandomHouse</option> 90 <% 91 } else if (Dictionary.MACMILLAN==WordLinker.getValidDictionary(request.getParameter("dict"))) { 92 %> 93 <option value="ICIBA">爱词霸</option> 94 <option value="YOUDAO">有道</option> 95 <option value="COLLINS">柯林斯</option> 96 <option value="WEBSTER">韦氏</option> 97 <option value="OXFORD">牛津</option> 98 <option value="CAMBRIDGE">剑桥</option> 99 <option value="MACMILLAN" selected="selected">麦克米伦</option> 100 <option value="HERITAGE">美国传统</option> 101 <option value="WIKTIONARY">维基词典</option> 102 <option value="WORDNET">WordNet</option> 103 <option value="RANDOMHOUSE">RandomHouse</option> 104 <% 105 } else if (Dictionary.HERITAGE==WordLinker.getValidDictionary(request.getParameter("dict"))) { 106 %> 107 <option value="ICIBA">爱词霸</option> 108 <option value="YOUDAO">有道</option> 109 <option value="COLLINS">柯林斯</option> 110 <option value="WEBSTER">韦氏</option> 111 <option value="OXFORD">牛津</option> 112 <option value="CAMBRIDGE">剑桥</option> 113 <option value="MACMILLAN">麦克米伦</option> 114 <option value="HERITAGE" selected="selected">美国传统</option> 115 <option value="WIKTIONARY">维基词典</option> 116 <option value="WORDNET">WordNet</option> 117 <option value="RANDOMHOUSE">RandomHouse</option> 118 <% 119 } else if (Dictionary.WIKTIONARY==WordLinker.getValidDictionary(request.getParameter("dict"))) { 120 %> 121 <option value="ICIBA">爱词霸</option> 122 <option value="YOUDAO">有道</option> 123 <option value="COLLINS">柯林斯</option> 124 <option value="WEBSTER">韦氏</option> 125 <option value="OXFORD">牛津</option> 126 <option value="CAMBRIDGE">剑桥</option> 127 <option value="MACMILLAN">麦克米伦</option> 128 <option value="HERITAGE">美国传统</option> 129 <option value="WIKTIONARY" selected="selected">维基词典</option> 130 <option value="WORDNET">WordNet</option> 131 <option value="RANDOMHOUSE">RandomHouse</option> 132 <% 133 } else if (Dictionary.WORDNET==WordLinker.getValidDictionary(request.getParameter("dict"))) { 134 %> 135 <option value="ICIBA">爱词霸</option> 136 <option value="YOUDAO">有道</option> 137 <option value="COLLINS">柯林斯</option> 138 <option value="WEBSTER">韦氏</option> 139 <option value="OXFORD">牛津</option> 140 <option value="CAMBRIDGE">剑桥</option> 141 <option value="MACMILLAN">麦克米伦</option> 142 <option value="HERITAGE">美国传统</option> 143 <option value="WIKTIONARY">维基词典</option> 144 <option value="WORDNET" selected="selected">WordNet</option> 145 <option value="RANDOMHOUSE">RandomHouse</option> 146 <% 147 } else if (Dictionary.RANDOMHOUSE==WordLinker.getValidDictionary(request.getParameter("dict"))) { 148 %> 149 <option value="ICIBA">爱词霸</option> 150 <option value="YOUDAO">有道</option> 151 <option value="COLLINS">柯林斯</option> 152 <option value="WEBSTER">韦氏</option> 153 <option value="OXFORD">牛津</option> 154 <option value="CAMBRIDGE">剑桥</option> 155 <option value="MACMILLAN">麦克米伦</option> 156 <option value="HERITAGE">美国传统</option> 157 <option value="WIKTIONARY">维基词典</option> 158 <option value="WORDNET">WordNet</option> 159 <option value="RANDOMHOUSE" selected="selected">RandomHouse</option> 160 <% 161 } 162 %> 163 </select>
这段代码有什么问题呢?
如果我们一次写好后就不需要维护和更改这段代码,那么根据自己的经验知识和理解快速实现功能,满足要求即可。
但是,如果需要维护代码和扩展功能呢?比如,我现在要把选项显示的所有的中文全部改为英文,那么就变成如下代码了:
1 <select name="dict" id="dict" onchange="update();"> 2 <% 3 if (Dictionary.ICIBA==WordLinker.getValidDictionary(request.getParameter("dict"))) { 4 %> 5 <option value="ICIBA" selected="selected">iCIBA</option> 6 <option value="YOUDAO">Youdao</option> 7 <option value="COLLINS">Collins</option> 8 <option value="WEBSTER">Webster's</option> 9 <option value="OXFORD">Oxford</option> 10 <option value="CAMBRIDGE">Cambridge</option> 11 <option value="MACMILLAN">Macmillan</option> 12 <option value="HERITAGE">Heritage</option> 13 <option value="WIKTIONARY">Wiktionary</option> 14 <option value="WORDNET">WordNet</option> 15 <option value="RANDOMHOUSE">RandomHouse</option> 16 <% 17 } else if (Dictionary.YOUDAO==WordLinker.getValidDictionary(request.getParameter("dict"))) { 18 %> 19 <option value="ICIBA">iCIBA</option> 20 <option value="YOUDAO" selected="selected">Youdao</option> 21 <option value="COLLINS">Collins</option> 22 <option value="WEBSTER">Webster's</option> 23 <option value="OXFORD">Oxford</option> 24 <option value="CAMBRIDGE">Cambridge</option> 25 <option value="MACMILLAN">Macmillan</option> 26 <option value="HERITAGE">Heritage</option> 27 <option value="WIKTIONARY">Wiktionary</option> 28 <option value="WORDNET">WordNet</option> 29 <option value="RANDOMHOUSE">RandomHouse</option> 30 <% 31 } else if (Dictionary.COLLINS==WordLinker.getValidDictionary(request.getParameter("dict"))) { 32 %> 33 <option value="ICIBA">iCIBA</option> 34 <option value="YOUDAO">Youdao</option> 35 <option value="COLLINS" selected="selected">Collins</option> 36 <option value="WEBSTER">Webster's</option> 37 <option value="OXFORD">Oxford</option> 38 <option value="CAMBRIDGE">Cambridge</option> 39 <option value="MACMILLAN">Macmillan</option> 40 <option value="HERITAGE">Heritage</option> 41 <option value="WIKTIONARY">Wiktionary</option> 42 <option value="WORDNET">WordNet</option> 43 <option value="RANDOMHOUSE">RandomHouse</option> 44 <% 45 } else if (Dictionary.WEBSTER==WordLinker.getValidDictionary(request.getParameter("dict"))) { 46 %> 47 <option value="ICIBA">iCIBA</option> 48 <option value="YOUDAO">Youdao</option> 49 <option value="COLLINS">Collins</option> 50 <option value="WEBSTER" selected="selected">Webster's</option> 51 <option value="OXFORD">Oxford</option> 52 <option value="CAMBRIDGE">Cambridge</option> 53 <option value="MACMILLAN">Macmillan</option> 54 <option value="HERITAGE">Heritage</option> 55 <option value="WIKTIONARY">Wiktionary</option> 56 <option value="WORDNET">WordNet</option> 57 <option value="RANDOMHOUSE">RandomHouse</option> 58 <% 59 } else if (Dictionary.OXFORD==WordLinker.getValidDictionary(request.getParameter("dict"))) { 60 %> 61 <option value="ICIBA">iCIBA</option> 62 <option value="YOUDAO">Youdao</option> 63 <option value="COLLINS">Collins</option> 64 <option value="WEBSTER">Webster's</option> 65 <option value="OXFORD" selected="selected">Oxford</option> 66 <option value="CAMBRIDGE">Cambridge</option> 67 <option value="MACMILLAN">Macmillan</option> 68 <option value="HERITAGE">Heritage</option> 69 <option value="WIKTIONARY">Wiktionary</option> 70 <option value="WORDNET">WordNet</option> 71 <option value="RANDOMHOUSE">RandomHouse</option> 72 <% 73 } else if (Dictionary.CAMBRIDGE==WordLinker.getValidDictionary(request.getParameter("dict"))) { 74 %> 75 <option value="ICIBA">iCIBA</option> 76 <option value="YOUDAO">Youdao</option> 77 <option value="COLLINS">Collins</option> 78 <option value="WEBSTER">Webster's</option> 79 <option value="OXFORD">Oxford</option> 80 <option value="CAMBRIDGE" selected="selected">Cambridge</option> 81 <option value="MACMILLAN">Macmillan</option> 82 <option value="HERITAGE">Heritage</option> 83 <option value="WIKTIONARY">Wiktionary</option> 84 <option value="WORDNET">WordNet</option> 85 <option value="RANDOMHOUSE">RandomHouse</option> 86 <% 87 } else if (Dictionary.MACMILLAN==WordLinker.getValidDictionary(request.getParameter("dict"))) { 88 %> 89 <option value="ICIBA">iCIBA</option> 90 <option value="YOUDAO">Youdao</option> 91 <option value="COLLINS">Collins</option> 92 <option value="WEBSTER">Webster's</option> 93 <option value="OXFORD">Oxford</option> 94 <option value="CAMBRIDGE">Cambridge</option> 95 <option value="MACMILLAN" selected="selected">Macmillan</option> 96 <option value="HERITAGE">Heritage</option> 97 <option value="WIKTIONARY">Wiktionary</option> 98 <option value="WORDNET">WordNet</option> 99 <option value="RANDOMHOUSE">RandomHouse</option> 100 <% 101 } else if (Dictionary.HERITAGE==WordLinker.getValidDictionary(request.getParameter("dict"))) { 102 %> 103 <option value="ICIBA">iCIBA</option> 104 <option value="YOUDAO">Youdao</option> 105 <option value="COLLINS">Collins</option> 106 <option value="WEBSTER">Webster's</option> 107 <option value="OXFORD">Oxford</option> 108 <option value="CAMBRIDGE">Cambridge</option> 109 <option value="MACMILLAN">Macmillan</option> 110 <option value="HERITAGE" selected="selected">Heritage</option> 111 <option value="WIKTIONARY">Wiktionary</option> 112 <option value="WORDNET">WordNet</option> 113 <option value="RANDOMHOUSE">RandomHouse</option> 114 <% 115 } else if (Dictionary.WIKTIONARY==WordLinker.getValidDictionary(request.getParameter("dict"))) { 116 %> 117 <option value="ICIBA">iCIBA</option> 118 <option value="YOUDAO">Youdao</option> 119 <option value="COLLINS">Collins</option> 120 <option value="WEBSTER">Webster's</option> 121 <option value="OXFORD">Oxford</option> 122 <option value="CAMBRIDGE">Cambridge</option> 123 <option value="MACMILLAN">Macmillan</option> 124 <option value="HERITAGE">Heritage</option> 125 <option value="WIKTIONARY" selected="selected">Wiktionary</option> 126 <option value="WORDNET">WordNet</option> 127 <option value="RANDOMHOUSE">RandomHouse</option> 128 <% 129 } else if (Dictionary.WORDNET==WordLinker.getValidDictionary(request.getParameter("dict"))) { 130 %> 131 <option value="ICIBA">iCIBA</option> 132 <option value="YOUDAO">Youdao</option> 133 <option value="COLLINS">Collins</option> 134 <option value="WEBSTER">Webster's</option> 135 <option value="OXFORD">Oxford</option> 136 <option value="CAMBRIDGE">Cambridge</option> 137 <option value="MACMILLAN">Macmillan</option> 138 <option value="HERITAGE">Heritage</option> 139 <option value="WIKTIONARY">Wiktionary</option> 140 <option value="WORDNET" selected="selected">WordNet</option> 141 <option value="RANDOMHOUSE">RandomHouse</option> 142 <% 143 } else if (Dictionary.RANDOMHOUSE==WordLinker.getValidDictionary(request.getParameter("dict"))) { 144 %> 145 <option value="ICIBA">iCIBA</option> 146 <option value="YOUDAO">Youdao</option> 147 <option value="COLLINS">Collins</option> 148 <option value="WEBSTER">Webster's</option> 149 <option value="OXFORD">Oxford</option> 150 <option value="CAMBRIDGE">Cambridge</option> 151 <option value="MACMILLAN">Macmillan</option> 152 <option value="HERITAGE">Heritage</option> 153 <option value="WIKTIONARY">Wiktionary</option> 154 <option value="WORDNET">WordNet</option> 155 <option value="RANDOMHOUSE" selected="selected">RandomHouse</option> 156 <% 157 } 158 %> 159 </select>
你可能说,我只需要执行 CTRL+R 11次,没啥问题啊?其实这恰恰就是问题,因为同样的字符串重复了11次。
改成了英文名称之后,接下来,如果我们要新增一个选项怎么办呢?我们不但需要复制粘贴代码来新增一个ELSE IF代码块,我们还需要修改既有的11个IF代码块,都新增一个option。这不但违反了DRY原则,也违反了开闭原则。下面开始来改造,改造后的代码如下:
1 <select name="dict" id="dict" onchange="update();"> 2 <% 3 if (Dictionary.ICIBA==WordLinker.getValidDictionary(request.getParameter("dict"))) { 4 %> 5 <option value="ICIBA" selected="selected">iCIBA</option> 6 <% 7 } else { 8 %> 9 <option value="ICIBA">iCIBA</option> 10 <% 11 } 12 if (Dictionary.YOUDAO==WordLinker.getValidDictionary(request.getParameter("dict"))) { 13 %> 14 <option value="YOUDAO" selected="selected">Youdao</option> 15 <% 16 } else { 17 %> 18 <option value="YOUDAO">Youdao</option> 19 <% 20 } 21 if (Dictionary.COLLINS==WordLinker.getValidDictionary(request.getParameter("dict"))) { 22 %> 23 <option value="COLLINS" selected="selected">Collins</option> 24 <% 25 } else { 26 %> 27 <option value="COLLINS">Collins</option> 28 <% 29 } 30 if (Dictionary.WEBSTER==WordLinker.getValidDictionary(request.getParameter("dict"))) { 31 %> 32 <option value="WEBSTER" selected="selected">Webster's</option> 33 <% 34 } else { 35 %> 36 <option value="WEBSTER">Webster's</option> 37 <% 38 } 39 if (Dictionary.OXFORD==WordLinker.getValidDictionary(request.getParameter("dict"))) { 40 %> 41 <option value="OXFORD" selected="selected">Oxford</option> 42 <% 43 } else { 44 %> 45 <option value="OXFORD">Oxford</option> 46 <% 47 } 48 if (Dictionary.CAMBRIDGE==WordLinker.getValidDictionary(request.getParameter("dict"))) { 49 %> 50 <option value="CAMBRIDGE" selected="selected">Cambridge</option> 51 <% 52 } else { 53 %> 54 <option value="CAMBRIDGE">Cambridge</option> 55 <% 56 } 57 if (Dictionary.MACMILLAN==WordLinker.getValidDictionary(request.getParameter("dict"))) { 58 %> 59 <option value="MACMILLAN" selected="selected">Macmillan</option> 60 <% 61 } else { 62 %> 63 <option value="MACMILLAN">Macmillan</option> 64 <% 65 } 66 if (Dictionary.HERITAGE==WordLinker.getValidDictionary(request.getParameter("dict"))) { 67 %> 68 <option value="HERITAGE" selected="selected">Heritage</option> 69 <% 70 } else { 71 %> 72 <option value="HERITAGE">Heritage</option> 73 <% 74 } 75 if (Dictionary.WIKTIONARY==WordLinker.getValidDictionary(request.getParameter("dict"))) { 76 %> 77 <option value="WIKTIONARY" selected="selected">Wiktionary</option> 78 <% 79 } else { 80 %> 81 <option value="WIKTIONARY">Wiktionary</option> 82 <% 83 } 84 if (Dictionary.WORDNET==WordLinker.getValidDictionary(request.getParameter("dict"))) { 85 %> 86 <option value="WORDNET" selected="selected">WordNet</option> 87 <% 88 } else { 89 %> 90 <option value="WORDNET">WordNet</option> 91 <% 92 } 93 if (Dictionary.RANDOMHOUSE==WordLinker.getValidDictionary(request.getParameter("dict"))) { 94 %> 95 <option value="RANDOMHOUSE" selected="selected">RandomHouse</option> 96 <% 97 } else { 98 %> 99 <option value="RANDOMHOUSE">RandomHouse</option> 100 <% 101 } 102 %> 103 </select>
重构代码后是不是可读性降低了?原来的代码虽然违反了DRY原则和开闭原则,但是可读性却是极好的。即使如此,重构后的代码却是非常容易扩展,新增选项只需要增加一个IF代码块即可,不需要修改现有的IF代码块,符合了开闭原则,同时移除了重复了11次的option,所以虽然可读性降低了,但是却完美达成了DRY原则和开闭原则。
这个例子说明了一个非常重要的道理,具体的东西更容易让人理解,而更抽象的东西理解起来就要更费劲,所以,我们在写代码的时候,一开始完全没有必要满脑子的各种最佳实践,各种设计模式,各种架构模式,最佳实践设计模式架构模式都有限制的场景,他们都应该是重构自然而然的结果,一开始就考虑这些东西,不但会拖慢功能实现的速度,而且往往会陷于“过度设计”、“不成熟的优化”的泥淖。
上面的代码还不够彻底,还存在重复,接着重构来贯彻DRY原则:
1 <select name="dict" id="dict" onchange="update();"> 2 <% 3 Dictionary selectedDictionary = WordLinker.getValidDictionary(request.getParameter("dict")); 4 if (Dictionary.ICIBA == selectedDictionary) { 5 %> 6 <option value="ICIBA" selected="selected">iCIBA</option> 7 <% 8 } else { 9 %> 10 <option value="ICIBA">iCIBA</option> 11 <% 12 } 13 14 if (Dictionary.YOUDAO == selectedDictionary) { 15 %> 16 <option value="YOUDAO" selected="selected">Youdao</option> 17 <% 18 } else { 19 %> 20 <option value="YOUDAO">Youdao</option> 21 <% 22 } 23 24 if (Dictionary.COLLINS == selectedDictionary) { 25 %> 26 <option value="COLLINS" selected="selected">Collins</option> 27 <% 28 } else { 29 %> 30 <option value="COLLINS">Collins</option> 31 <% 32 } 33 34 if (Dictionary.WEBSTER == selectedDictionary) { 35 %> 36 <option value="WEBSTER" selected="selected">Webster's</option> 37 <% 38 } else { 39 %> 40 <option value="WEBSTER">Webster's</option> 41 <% 42 } 43 44 if (Dictionary.OXFORD == selectedDictionary) { 45 %> 46 <option value="OXFORD" selected="selected">Oxford</option> 47 <% 48 } else { 49 %> 50 <option value="OXFORD">Oxford</option> 51 <% 52 } 53 54 if (Dictionary.CAMBRIDGE == selectedDictionary) { 55 %> 56 <option value="CAMBRIDGE" selected="selected">Cambridge</option> 57 <% 58 } else { 59 %> 60 <option value="CAMBRIDGE">Cambridge</option> 61 <% 62 } 63 64 if (Dictionary.MACMILLAN == selectedDictionary) { 65 %> 66 <option value="MACMILLAN" selected="selected">Macmillan</option> 67 <% 68 } else { 69 %> 70 <option value="MACMILLAN">Macmillan</option> 71 <% 72 } 73 74 if (Dictionary.HERITAGE == selectedDictionary) { 75 %> 76 <option value="HERITAGE" selected="selected">Heritage</option> 77 <% 78 } else { 79 %> 80 <option value="HERITAGE">Heritage</option> 81 <% 82 } 83 84 if (Dictionary.WIKTIONARY == selectedDictionary) { 85 %> 86 <option value="WIKTIONARY" selected="selected">Wiktionary</option> 87 <% 88 } else { 89 %> 90 <option value="WIKTIONARY">Wiktionary</option> 91 <% 92 } 93 94 if (Dictionary.WORDNET == selectedDictionary) { 95 %> 96 <option value="WORDNET" selected="selected">WordNet</option> 97 <% 98 } else { 99 %> 100 <option value="WORDNET">WordNet</option> 101 <% 102 } 103 104 if (Dictionary.RANDOMHOUSE == selectedDictionary) { 105 %> 106 <option value="RANDOMHOUSE" selected="selected">RandomHouse</option> 107 <% 108 } else { 109 %> 110 <option value="RANDOMHOUSE">RandomHouse</option> 111 <% 112 } 113 %> 114 </select>
这次,我们把重复的代码抽取为一个变量,并在IF代码块前面加了一个空行,看上去是不是可读性提高了?这也说明了DRY原则对于代码的简洁和易读是有帮助的,同时也说明了我们编写的代码的换行缩进等风格对可读性也是有影响的,作为一个有尊严的程序员,需要注意这些细节,细节决定成败,成功者往往只是比对手多做了一点而已,而多做的这一点,往往就是在细节上面。
虽然易读性有了提高,但是还是比较糟糕,离生成的HTML的差距还很大,下面继续重构,结果如下:
1<% 2 Dictionary selectedDictionary = WordLinker.getValidDictionary(request.getParameter("dict")); 3 String ICIBA = ""; 4 String YOUDAO = ""; 5 String COLLINS = ""; 6 String WEBSTER = ""; 7 String OXFORD = ""; 8 String CAMBRIDGE = ""; 9 String MACMILLAN = ""; 10 String HERITAGE = ""; 11 String WIKTIONARY = ""; 12 String WORDNET = ""; 13 String RANDOMHOUSE = ""; 14 String selected = "selected=\"selected\""; 15 switch (selectedDictionary){ 16 case ICIBA: 17 ICIBA = selected; break; 18 case YOUDAO: 19 YOUDAO = selected; break; 20 case COLLINS: 21 COLLINS = selected; break; 22 case WEBSTER: 23 WEBSTER = selected; break; 24 case OXFORD: 25 OXFORD = selected; break; 26 case CAMBRIDGE: 27 CAMBRIDGE = selected; break; 28 case MACMILLAN: 29 MACMILLAN = selected; break; 30 case HERITAGE: 31 HERITAGE = selected; break; 32 case WIKTIONARY: 33 WIKTIONARY = selected; break; 34 case WORDNET: 35 WORDNET = selected; break; 36 case RANDOMHOUSE: 37 RANDOMHOUSE = selected; break; 38 } 39%> 40<select name="dict" id="dict" onchange="update();"> 41 <option value="ICIBA" <%=ICIBA%>>iCIBA</option> 42 <option value="YOUDAO" <%=YOUDAO%>>Youdao</option> 43 <option value="COLLINS" <%=COLLINS%>>Collins</option> 44 <option value="WEBSTER" <%=WEBSTER%>>Webster's</option> 45 <option value="OXFORD" <%=OXFORD%>>Oxford</option> 46 <option value="CAMBRIDGE" <%=CAMBRIDGE%>>Cambridge</option> 47 <option value="MACMILLAN" <%=MACMILLAN%>>Macmillan</option> 48 <option value="HERITAGE" <%=HERITAGE%>>Heritage</option> 49 <option value="WIKTIONARY" <%=WIKTIONARY%>>Wiktionary</option> 50 <option value="WORDNET" <%=WORDNET%>>WordNet</option> 51 <option value="RANDOMHOUSE" <%=RANDOMHOUSE%>>RandomHouse</option> 52</select>
这次重构过后,看起来是不是跟最终生成的HTML差别已经不大了?易读性有了质的飞跃,但是你有没有发现,这里面还是有很多重复,而且存在着模式和规律,我们可以再继续重构吗?
肯定可以,直接看重构后的结果吧:
1 <select name="dict" id="dict" onchange="update();"> 2 <% 3 Dictionary selectedDictionary = WordLinker.getValidDictionary(request.getParameter("dict")); 4 for(Dictionary dictionary : Dictionary.values()){ 5 if (dictionary == selectedDictionary) { 6 %> 7 <option value="<%=dictionary.name()%>" selected="selected"><%=dictionary.getDes()%></option> 8 <% 9 } else { 10 %> 11 <option value="<%=dictionary.name()%>"><%=dictionary.getDes()%></option> 12 <% 13 } 14 } 15 %> 16 </select>
这次,我们直接遍历数据模型Dictionary,从而完全消除了重复,同时在开闭原则上也有了新的高度,以后当我们需要扩展新的选项的时候,我们已经不再需要修改哪怕一行代码了。
那如果我们不是需要Dictionary的所有选项怎么办呢?这肯定也不是问题,解决方法如下:
1<%! 2 private static final List<Dictionary> DICTIONARIES = Arrays.asList(Dictionary.ICIBA, Dictionary.YOUDAO, Dictionary.WEBSTER, Dictionary.OXFORD); 3%> 4<select name="dictionary" id="dictionary" onchange="update();"> 5 <% 6 Dictionary selectedDictionary = WordLinker.getValidDictionary(request.getParameter("dictionary")); 7 for(Dictionary dictionary : DICTIONARIES){ 8 if (dictionary == selectedDictionary) { 9 %> 10 <option value="<%=dictionary.name()%>" selected="selected"><%=dictionary.getDes()%></option> 11 <% 12 } else { 13 %> 14 <option value="<%=dictionary.name()%>"><%=dictionary.getDes()%></option> 15 <% 16 } 17 } 18 %> 19</select>
一次完美的重构就此结束,要想尝试更多的重构工作,敬请加入到superword的重构中来:https://github.com/ysc/superword