pdd-skills 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/README.md +1478 -0
  2. package/bin/pdd.js +354 -0
  3. package/config/bpmn-rules.yaml +166 -0
  4. package/config/checkstyle.xml +105 -0
  5. package/config/eslint.config.js +48 -0
  6. package/config/pmd.xml +91 -0
  7. package/config/prd-rules.yaml +113 -0
  8. package/config/ruff.toml +45 -0
  9. package/config/sqlfluff.cfg +82 -0
  10. package/hooks/hook-executor.js +332 -0
  11. package/index.js +43 -0
  12. package/lib/api-routes.js +750 -0
  13. package/lib/api-server.js +408 -0
  14. package/lib/cache/cache-config.js +209 -0
  15. package/lib/cache/system-cache.js +852 -0
  16. package/lib/config-manager.js +373 -0
  17. package/lib/generate.js +528 -0
  18. package/lib/grpc/grpc-routes.js +1134 -0
  19. package/lib/grpc/grpc-server.js +912 -0
  20. package/lib/grpc/proto-definitions.js +1033 -0
  21. package/lib/init.js +172 -0
  22. package/lib/iteration/auto-fixer.js +1025 -0
  23. package/lib/iteration/auto-reviewer.js +923 -0
  24. package/lib/iteration/controller.js +577 -0
  25. package/lib/list.js +130 -0
  26. package/lib/mcp-server.js +548 -0
  27. package/lib/openclaw/api-integration.js +535 -0
  28. package/lib/openclaw/cli-integration.js +567 -0
  29. package/lib/openclaw/data-sync.js +845 -0
  30. package/lib/openclaw/openclaw-adapter.js +783 -0
  31. package/lib/plugin/example-plugins/code-stats/index.js +332 -0
  32. package/lib/plugin/example-plugins/code-stats/plugin.json +1 -0
  33. package/lib/plugin/example-plugins/custom-linter/index.js +472 -0
  34. package/lib/plugin/example-plugins/custom-linter/plugin.json +1 -0
  35. package/lib/plugin/example-plugins/hello-world/index.js +86 -0
  36. package/lib/plugin/example-plugins/hello-world/plugin.json +1 -0
  37. package/lib/plugin/plugin-manager.js +655 -0
  38. package/lib/plugin/plugin-sdk.js +565 -0
  39. package/lib/plugin/sandbox.js +627 -0
  40. package/lib/quality/rules/maintainability.js +418 -0
  41. package/lib/quality/rules/performance.js +498 -0
  42. package/lib/quality/rules/readability.js +441 -0
  43. package/lib/quality/rules/robustness.js +504 -0
  44. package/lib/quality/rules/security.js +444 -0
  45. package/lib/quality/scorer.js +576 -0
  46. package/lib/report.js +669 -0
  47. package/lib/sdk-base.js +301 -0
  48. package/lib/sdk-js.js +446 -0
  49. package/lib/sdk-python/README.md +546 -0
  50. package/lib/sdk-python/examples/basic_usage.py +450 -0
  51. package/lib/sdk-python/pdd_sdk/__init__.py +180 -0
  52. package/lib/sdk-python/pdd_sdk/client.py +1170 -0
  53. package/lib/sdk-python/pdd_sdk/events.py +423 -0
  54. package/lib/sdk-python/pdd_sdk/exceptions.py +158 -0
  55. package/lib/sdk-python/pdd_sdk/models.py +518 -0
  56. package/lib/sdk-python/pdd_sdk/utils.py +759 -0
  57. package/lib/token/budget-alert.js +367 -0
  58. package/lib/token/budget-manager.js +485 -0
  59. package/lib/update.js +54 -0
  60. package/lib/utils/logger.js +88 -0
  61. package/lib/verify.js +741 -0
  62. package/lib/version.js +52 -0
  63. package/lib/vm/README.md +102 -0
  64. package/lib/vm/dashboard/api-routes.js +669 -0
  65. package/lib/vm/dashboard/server.js +391 -0
  66. package/lib/vm/dashboard/sse.js +358 -0
  67. package/lib/vm/dashboard/static/css/dashboard.css +1378 -0
  68. package/lib/vm/dashboard/static/index.html +118 -0
  69. package/lib/vm/dashboard/static/js/app.js +949 -0
  70. package/lib/vm/dashboard/static/js/charts.js +913 -0
  71. package/lib/vm/dashboard/static/js/kanban-view.js +1053 -0
  72. package/lib/vm/dashboard/static/js/pipeline-view.js +463 -0
  73. package/lib/vm/dashboard/static/js/quality-view.js +598 -0
  74. package/lib/vm/dashboard/static/js/system-view.js +1021 -0
  75. package/lib/vm/data-provider.js +1191 -0
  76. package/lib/vm/event-bus.js +402 -0
  77. package/lib/vm/hooks/extract-hook.js +307 -0
  78. package/lib/vm/hooks/generate-hook.js +374 -0
  79. package/lib/vm/hooks/hook-interface.js +458 -0
  80. package/lib/vm/hooks/report-hook.js +331 -0
  81. package/lib/vm/hooks/verify-hook.js +454 -0
  82. package/lib/vm/models.js +1003 -0
  83. package/lib/vm/reconciler.js +855 -0
  84. package/lib/vm/scanner.js +988 -0
  85. package/lib/vm/state-schema.js +955 -0
  86. package/lib/vm/state-store.js +733 -0
  87. package/lib/vm/tui/components/card.js +339 -0
  88. package/lib/vm/tui/components/progress-bar.js +368 -0
  89. package/lib/vm/tui/components/sparkline.js +327 -0
  90. package/lib/vm/tui/components/status-light.js +294 -0
  91. package/lib/vm/tui/components/table.js +370 -0
  92. package/lib/vm/tui/input.js +335 -0
  93. package/lib/vm/tui/renderer.js +548 -0
  94. package/lib/vm/tui/screens/kanban-screen.js +397 -0
  95. package/lib/vm/tui/screens/overview-screen.js +357 -0
  96. package/lib/vm/tui/screens/quality-screen.js +336 -0
  97. package/lib/vm/tui/screens/system-screen.js +379 -0
  98. package/lib/vm/tui/tui.js +805 -0
  99. package/package.json +1 -0
  100. package/scripts/cso-analyzer.js +198 -0
  101. package/scripts/eval-runner.js +359 -0
  102. package/scripts/i18n-checker.js +109 -0
  103. package/scripts/linter/activiti-linter.js +272 -0
  104. package/scripts/linter/prd-linter.js +162 -0
  105. package/scripts/linter/report-generator.js +207 -0
  106. package/scripts/linter/run-linters.js +285 -0
  107. package/scripts/linter/sql-linter.js +166 -0
  108. package/scripts/token-analyzer.js +162 -0
  109. package/scripts/vm-test.js +180 -0
  110. package/skills/core/official-doc-writer/LICENSE +21 -0
  111. package/skills/core/official-doc-writer/README.md +232 -0
  112. package/skills/core/official-doc-writer/SKILL.md +475 -0
  113. package/skills/core/official-doc-writer/_meta.json +1 -0
  114. package/skills/core/official-doc-writer/document_generator.py +580 -0
  115. package/skills/core/official-doc-writer/evals/default-evals.json +1 -0
  116. package/skills/core/official-doc-writer/examples.md +150 -0
  117. package/skills/core/official-doc-writer/fonts/FONTS_LIST.md +45 -0
  118. package/skills/core/official-doc-writer/fonts/README.md +141 -0
  119. package/skills/core/official-doc-writer/fonts/SIMFANG.TTF +0 -0
  120. package/skills/core/official-doc-writer/fonts/SIMHEI.TTF +0 -0
  121. package/skills/core/official-doc-writer/fonts/SIMKAI.TTF +0 -0
  122. package/skills/core/official-doc-writer/fonts/SIMSUN.TTC +0 -0
  123. package/skills/core/official-doc-writer/fonts//346/226/271/346/255/243/345/260/217/346/240/207/345/256/213GBK.TTF +0 -0
  124. package/skills/core/official-doc-writer/references/GBT_9704-2012_/345/205/232/346/224/277/346/234/272/345/205/263/345/205/254/346/226/207/346/240/274/345/274/217.md +422 -0
  125. package/skills/core/official-doc-writer/scripts/__pycache__/generate_official_doc.cpython-313.pyc +0 -0
  126. package/skills/core/official-doc-writer/scripts/dialog_manager.py +564 -0
  127. package/skills/core/official-doc-writer/scripts/generate_official_doc.py +252 -0
  128. package/skills/core/official-doc-writer/scripts/install_fonts.py +390 -0
  129. package/skills/core/official-doc-writer/scripts/smart_prompts.py +363 -0
  130. package/skills/core/pdd-ba/SKILL.md +305 -0
  131. package/skills/core/pdd-ba/_meta.json +1 -0
  132. package/skills/core/pdd-ba/evals/default-evals.json +1 -0
  133. package/skills/core/pdd-code-reviewer/SKILL.md +378 -0
  134. package/skills/core/pdd-code-reviewer/_meta.json +1 -0
  135. package/skills/core/pdd-code-reviewer/evals/default-evals.json +1 -0
  136. package/skills/core/pdd-doc-change/SKILL.md +350 -0
  137. package/skills/core/pdd-doc-change/_meta.json +1 -0
  138. package/skills/core/pdd-doc-change/evals/default-evals.json +1 -0
  139. package/skills/core/pdd-doc-gardener/SKILL.md +248 -0
  140. package/skills/core/pdd-doc-gardener/_meta.json +1 -0
  141. package/skills/core/pdd-doc-gardener/evals/default-evals.json +1 -0
  142. package/skills/core/pdd-entropy-reduction/SKILL.md +360 -0
  143. package/skills/core/pdd-entropy-reduction/_meta.json +1 -0
  144. package/skills/core/pdd-entropy-reduction/evals/default-evals.json +1 -0
  145. package/skills/core/pdd-entropy-reduction/references/entropy-report-template.md +287 -0
  146. package/skills/core/pdd-entropy-reduction/references/golden-principles.md +573 -0
  147. package/skills/core/pdd-entropy-reduction/scripts/entropy_scan.py +712 -0
  148. package/skills/core/pdd-extract-features/SKILL.md +320 -0
  149. package/skills/core/pdd-extract-features/_meta.json +1 -0
  150. package/skills/core/pdd-extract-features/evals/default-evals.json +1 -0
  151. package/skills/core/pdd-generate-spec/SKILL.md +418 -0
  152. package/skills/core/pdd-generate-spec/_meta.json +1 -0
  153. package/skills/core/pdd-generate-spec/evals/default-evals.json +1 -0
  154. package/skills/core/pdd-implement-feature/SKILL.md +332 -0
  155. package/skills/core/pdd-implement-feature/_meta.json +1 -0
  156. package/skills/core/pdd-implement-feature/evals/default-evals.json +1 -0
  157. package/skills/core/pdd-main/SKILL.md +540 -0
  158. package/skills/core/pdd-main/_meta.json +1 -0
  159. package/skills/core/pdd-main/evals/default-evals.json +1 -0
  160. package/skills/core/pdd-main/evals/evals.json +215 -0
  161. package/skills/core/pdd-verify-feature/SKILL.md +474 -0
  162. package/skills/core/pdd-verify-feature/_meta.json +1 -0
  163. package/skills/core/pdd-verify-feature/evals/default-evals.json +1 -0
  164. package/skills/core/pdd-vm/evals/default-evals.json +1 -0
  165. package/skills/core/traffic-accident-assessor/LICENSE +29 -0
  166. package/skills/core/traffic-accident-assessor/SKILL.md +439 -0
  167. package/skills/core/traffic-accident-assessor/evals/evals.json +1 -0
  168. package/skills/core/traffic-accident-assessor/references/accident-types.md +369 -0
  169. package/skills/core/traffic-accident-assessor/references/liability-rules.md +287 -0
  170. package/skills/core/traffic-accident-assessor/references/traffic-laws.md +226 -0
  171. package/skills/core/traffic-accident-assessor/references//351/253/230/345/260/224/345/244/253/350/257/264/346/230/216/344/271/246.pdf +32576 -106
  172. package/skills/core/traffic-accident-assessor/scripts/generate_official_statement.py +588 -0
  173. package/skills/core/traffic-accident-assessor/scripts/generate_report.py +495 -0
  174. package/skills/core/traffic-accident-assessor/scripts/generate_statement.py +528 -0
  175. package/skills/core/traffic-accident-assessor.zip +0 -0
  176. package/skills/entropy/expert-arch-enforcer/SKILL.md +292 -0
  177. package/skills/entropy/expert-arch-enforcer/_meta.json +1 -0
  178. package/skills/entropy/expert-arch-enforcer/evals/default-evals.json +1 -0
  179. package/skills/entropy/expert-auto-refactor/SKILL.md +327 -0
  180. package/skills/entropy/expert-auto-refactor/_meta.json +1 -0
  181. package/skills/entropy/expert-auto-refactor/evals/default-evals.json +1 -0
  182. package/skills/entropy/expert-code-quality/SKILL.md +468 -0
  183. package/skills/entropy/expert-code-quality/_meta.json +1 -0
  184. package/skills/entropy/expert-code-quality/evals/default-evals.json +1 -0
  185. package/skills/entropy/expert-code-quality/evals/evals.json +109 -0
  186. package/skills/entropy/expert-code-quality/references/code-smells.md +605 -0
  187. package/skills/entropy/expert-code-quality/references/design-patterns.md +1111 -0
  188. package/skills/entropy/expert-code-quality/references/refactoring-catalog.md +1281 -0
  189. package/skills/entropy/expert-code-quality/references/solid-principles.md +524 -0
  190. package/skills/entropy/expert-entropy-auditor/SKILL.md +276 -0
  191. package/skills/entropy/expert-entropy-auditor/_meta.json +1 -0
  192. package/skills/entropy/expert-entropy-auditor/evals/default-evals.json +1 -0
  193. package/skills/expert/expert-activiti/SKILL.md +497 -0
  194. package/skills/expert/expert-activiti/_meta.json +1 -0
  195. package/skills/expert/expert-mysql/SKILL.md +832 -0
  196. package/skills/expert/expert-mysql/_meta.json +1 -0
  197. package/skills/expert/expert-performance/SKILL.md +379 -0
  198. package/skills/expert/expert-performance/_meta.json +1 -0
  199. package/skills/expert/expert-performance/evals/default-evals.json +1 -0
  200. package/skills/expert/expert-ruoyi/SKILL.md +472 -0
  201. package/skills/expert/expert-ruoyi/_meta.json +1 -0
  202. package/skills/expert/expert-security/SKILL.md +1341 -0
  203. package/skills/expert/expert-security/_meta.json +1 -0
  204. package/skills/expert/expert-security/evals/default-evals.json +1 -0
  205. package/skills/expert/software-architect/SKILL.md +350 -0
  206. package/skills/expert/software-architect/_meta.json +1 -0
  207. package/skills/expert/software-engineer/SKILL.md +437 -0
  208. package/skills/expert/software-engineer/_meta.json +1 -0
  209. package/skills/expert/software-engineer/architecture.md +130 -0
  210. package/skills/expert/software-engineer/patterns.md +151 -0
  211. package/skills/expert/software-engineer/testing.md +135 -0
  212. package/skills/expert/system-architect/SKILL.md +628 -0
  213. package/skills/expert/system-architect/_meta.json +1 -0
  214. package/skills/expert/system-architect/assets/templates/ARCHITECTURE.md +25 -0
  215. package/skills/expert/system-architect/assets/templates/README.md +44 -0
  216. package/skills/expert/system-architect/references/js-ts-standards.md +18 -0
  217. package/skills/expert/system-architect/references/python-standards.md +19 -0
  218. package/skills/expert/system-architect/references/scaffolding.md +61 -0
  219. package/skills/expert/system-architect/references/security-checklist.md +21 -0
  220. package/skills/openspec/openspec-apply-change/SKILL.md +156 -0
  221. package/skills/openspec/openspec-apply-change/_meta.json +1 -0
  222. package/skills/openspec/openspec-archive-change/SKILL.md +114 -0
  223. package/skills/openspec/openspec-archive-change/_meta.json +1 -0
  224. package/skills/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
  225. package/skills/openspec/openspec-bulk-archive-change/_meta.json +1 -0
  226. package/skills/openspec/openspec-continue-change/SKILL.md +118 -0
  227. package/skills/openspec/openspec-continue-change/_meta.json +1 -0
  228. package/skills/openspec/openspec-explore/SKILL.md +288 -0
  229. package/skills/openspec/openspec-explore/_meta.json +1 -0
  230. package/skills/openspec/openspec-ff-change/SKILL.md +101 -0
  231. package/skills/openspec/openspec-ff-change/_meta.json +1 -0
  232. package/skills/openspec/openspec-new-change/SKILL.md +74 -0
  233. package/skills/openspec/openspec-new-change/_meta.json +1 -0
  234. package/skills/openspec/openspec-onboard/SKILL.md +554 -0
  235. package/skills/openspec/openspec-onboard/_meta.json +1 -0
  236. package/skills/openspec/openspec-sync-specs/SKILL.md +138 -0
  237. package/skills/openspec/openspec-sync-specs/_meta.json +1 -0
  238. package/skills/openspec/openspec-verify-change/SKILL.md +168 -0
  239. package/skills/openspec/openspec-verify-change/_meta.json +1 -0
  240. package/skills/pr/pdd-multi-review/SKILL.md +534 -0
  241. package/skills/pr/pdd-multi-review/_meta.json +1 -0
  242. package/skills/pr/pdd-pr-batch/SKILL.md +303 -0
  243. package/skills/pr/pdd-pr-batch/_meta.json +1 -0
  244. package/skills/pr/pdd-pr-create/SKILL.md +344 -0
  245. package/skills/pr/pdd-pr-create/_meta.json +1 -0
  246. package/skills/pr/pdd-pr-merge/SKILL.md +286 -0
  247. package/skills/pr/pdd-pr-merge/_meta.json +1 -0
  248. package/skills/pr/pdd-pr-review/SKILL.md +217 -0
  249. package/skills/pr/pdd-pr-review/_meta.json +1 -0
  250. package/skills/pr/pdd-task-manager/SKILL.md +636 -0
  251. package/skills/pr/pdd-task-manager/_meta.json +1 -0
  252. package/skills/pr/pdd-template-engine/SKILL.md +306 -0
  253. package/skills/pr/pdd-template-engine/_meta.json +1 -0
  254. package/templates/behavior-shaping/iron-law-template.md +87 -0
  255. package/templates/behavior-shaping/rationalization-template.md +62 -0
  256. package/templates/behavior-shaping/red-flags-template.md +70 -0
  257. package/templates/bilingual-template.md +139 -0
  258. package/templates/config/default.yaml +47 -0
  259. package/templates/project/default/README.md +31 -0
  260. package/templates/project/frontend/README.md +46 -0
  261. package/templates/project/java/README.md +48 -0
@@ -0,0 +1,1281 @@
1
+ # Refactoring Catalog
2
+
3
+ A comprehensive catalog of refactoring techniques from Martin Fowler's "Refactoring: Improving the Design of Existing Code".
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Composing Methods](#1-composing-methods)
8
+ 2. [Moving Features Between Objects](#2-moving-features-between-objects)
9
+ 3. [Organizing Data](#3-organizing-data)
10
+ 4. [Simplifying Conditional Expressions](#4-simplifying-conditional-expressions)
11
+ 5. [Simplifying Method Calls](#5-simplifying-method-calls)
12
+ 6. [Dealing with Generalization](#6-dealing-with-generalization)
13
+
14
+ ---
15
+
16
+ ## 1. Composing Methods
17
+
18
+ ### Extract Method
19
+
20
+ **Problem**: You have a code fragment that can be grouped together.
21
+
22
+ **Solution**: Turn the fragment into a method whose name explains the purpose of the method.
23
+
24
+ ```java
25
+ // Before
26
+ void printOwing() {
27
+ System.out.println("***********************");
28
+ System.out.println("*** Customer Owes ***");
29
+ System.out.println("***********************");
30
+ // ... more code
31
+ }
32
+
33
+ // After
34
+ void printOwing() {
35
+ printBanner();
36
+ // ... more code
37
+ }
38
+
39
+ void printBanner() {
40
+ System.out.println("***********************");
41
+ System.out.println("*** Customer Owes ***");
42
+ System.out.println("***********************");
43
+ }
44
+ ```
45
+
46
+ **Mechanics**:
47
+ 1. Create a new method, name it after the intention
48
+ 2. Copy the extracted code to the new method
49
+ 3. Replace the original code with a call to the method
50
+ 4. Compile and test
51
+
52
+ ---
53
+
54
+ ### Inline Method
55
+
56
+ **Problem**: A method's body is just as clear as its name.
57
+
58
+ **Solution**: Put the method's body into the body of its callers and remove the method.
59
+
60
+ ```java
61
+ // Before
62
+ int getRating() {
63
+ return moreThanFiveLateDeliveries() ? 2 : 1;
64
+ }
65
+ boolean moreThanFiveLateDeliveries() {
66
+ return numberOfLateDeliveries > 5;
67
+ }
68
+
69
+ // After
70
+ int getRating() {
71
+ return numberOfLateDeliveries > 5 ? 2 : 1;
72
+ }
73
+ ```
74
+
75
+ ---
76
+
77
+ ### Extract Variable
78
+
79
+ **Problem**: You have a complex expression.
80
+
81
+ **Solution**: Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose.
82
+
83
+ ```java
84
+ // Before
85
+ if (platform.toUpperCase().indexOf("MAC") > -1 &&
86
+ browser.toUpperCase().indexOf("IE") > -1 &&
87
+ wasInitialized() && resize > 0) {
88
+ // do something
89
+ }
90
+
91
+ // After
92
+ final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
93
+ final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
94
+ final boolean wasResized = resize > 0;
95
+
96
+ if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
97
+ // do something
98
+ }
99
+ ```
100
+
101
+ ---
102
+
103
+ ### Replace Temp with Query
104
+
105
+ **Problem**: You are using a temporary variable to hold the result of an expression.
106
+
107
+ **Solution**: Extract the expression into a method. Replace all references to the temp with the expression. The new method can then be used in other methods.
108
+
109
+ ```java
110
+ // Before
111
+ double getPrice() {
112
+ int basePrice = quantity * itemPrice;
113
+ double discountFactor;
114
+ if (basePrice > 1000) discountFactor = 0.95;
115
+ else discountFactor = 0.98;
116
+ return basePrice * discountFactor;
117
+ }
118
+
119
+ // After
120
+ double getPrice() {
121
+ return basePrice() * discountFactor();
122
+ }
123
+
124
+ private int basePrice() {
125
+ return quantity * itemPrice;
126
+ }
127
+
128
+ private double discountFactor() {
129
+ if (basePrice() > 1000) return 0.95;
130
+ else return 0.98;
131
+ }
132
+ ```
133
+
134
+ ---
135
+
136
+ ### Introduce Explaining Variable
137
+
138
+ **Problem**: You have a complicated expression.
139
+
140
+ **Solution**: Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose.
141
+
142
+ ```java
143
+ // Before
144
+ if ((platform.toUpperCase().indexOf("MAC") > -1) &&
145
+ (browser.toUpperCase().indexOf("IE") > -1) &&
146
+ wasInitialized() && resize > 0) {
147
+ // do something
148
+ }
149
+
150
+ // After
151
+ final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
152
+ final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
153
+ final boolean wasResized = resize > 0;
154
+
155
+ if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
156
+ // do something
157
+ }
158
+ ```
159
+
160
+ ---
161
+
162
+ ### Split Temporary Variable
163
+
164
+ **Problem**: You have a temporary variable assigned to more than once, but is not a loop variable nor a collecting temporary variable.
165
+
166
+ **Solution**: Make a separate temporary variable for each assignment.
167
+
168
+ ```java
169
+ // Before
170
+ double temp = 2 * (height + width);
171
+ System.out.println(temp);
172
+ temp = height * width;
173
+ System.out.println(temp);
174
+
175
+ // After
176
+ final double perimeter = 2 * (height + width);
177
+ System.out.println(perimeter);
178
+ final double area = height * width;
179
+ System.out.println(area);
180
+ ```
181
+
182
+ ---
183
+
184
+ ### Remove Assignments to Parameters
185
+
186
+ **Problem**: The code assigns to a parameter.
187
+
188
+ **Solution**: Use a temporary variable instead.
189
+
190
+ ```java
191
+ // Before
192
+ int discount(int inputVal, int quantity, int yearToDate) {
193
+ if (inputVal > 50) inputVal -= 2;
194
+ // ...
195
+ }
196
+
197
+ // After
198
+ int discount(int inputVal, int quantity, int yearToDate) {
199
+ int result = inputVal;
200
+ if (inputVal > 50) result -= 2;
201
+ // ...
202
+ }
203
+ ```
204
+
205
+ ---
206
+
207
+ ### Replace Method with Method Object
208
+
209
+ **Problem**: You have a long method that uses local variables in such a way that you cannot apply Extract Method.
210
+
211
+ **Solution**: Turn the method into its own object so that all the local variables become fields on that object. You can then decompose the method into other methods on the same object.
212
+
213
+ ```java
214
+ // Before
215
+ class Order {
216
+ double price() {
217
+ double primaryBasePrice;
218
+ double secondaryBasePrice;
219
+ double tertiaryBasePrice;
220
+ // long computation
221
+ }
222
+ }
223
+
224
+ // After
225
+ class Order {
226
+ double price() {
227
+ return new PriceCalculator(this).compute();
228
+ }
229
+ }
230
+
231
+ class PriceCalculator {
232
+ private double primaryBasePrice;
233
+ private double secondaryBasePrice;
234
+ private double tertiaryBasePrice;
235
+
236
+ PriceCalculator(Order order) {
237
+ // initialize fields
238
+ }
239
+
240
+ double compute() {
241
+ // computation
242
+ }
243
+ }
244
+ ```
245
+
246
+ ---
247
+
248
+ ### Substitute Algorithm
249
+
250
+ **Problem**: You want to replace an algorithm with one that is clearer.
251
+
252
+ **Solution**: Replace the body of the method that implements the algorithm with a new algorithm.
253
+
254
+ ```java
255
+ // Before
256
+ String foundPerson(String[] people) {
257
+ for (int i = 0; i < people.length; i++) {
258
+ if (people[i].equals("Don")) {
259
+ return "Don";
260
+ }
261
+ if (people[i].equals("John")) {
262
+ return "John";
263
+ }
264
+ if (people[i].equals("Kent")) {
265
+ return "Kent";
266
+ }
267
+ }
268
+ return "";
269
+ }
270
+
271
+ // After
272
+ String foundPerson(String[] people) {
273
+ List<String> candidates = Arrays.asList("Don", "John", "Kent");
274
+ for (String person : people) {
275
+ if (candidates.contains(person)) {
276
+ return person;
277
+ }
278
+ }
279
+ return "";
280
+ }
281
+ ```
282
+
283
+ ---
284
+
285
+ ## 2. Moving Features Between Objects
286
+
287
+ ### Move Method
288
+
289
+ **Problem**: A method is, or will be, using or used by more features of another class than the class on which it is defined.
290
+
291
+ **Solution**: Create a new method with a similar body in the class it uses most. Either turn the old method into a simple delegation, or remove it altogether.
292
+
293
+ ```java
294
+ // Before
295
+ class Account {
296
+ double overdraftCharge() {
297
+ if (type.isPremium()) {
298
+ double result = 10;
299
+ if (daysOverdrawn > 7) {
300
+ result += (daysOverdrawn - 7) * 0.85;
301
+ }
302
+ return result;
303
+ }
304
+ return daysOverdrawn * 1.75;
305
+ }
306
+ }
307
+
308
+ // After
309
+ class AccountType {
310
+ double overdraftCharge(int daysOverdrawn) {
311
+ if (isPremium()) {
312
+ double result = 10;
313
+ if (daysOverdrawn > 7) {
314
+ result += (daysOverdrawn - 7) * 0.85;
315
+ }
316
+ return result;
317
+ }
318
+ return daysOverdrawn * 1.75;
319
+ }
320
+ }
321
+ ```
322
+
323
+ ---
324
+
325
+ ### Move Field
326
+
327
+ **Problem**: A field is, or will be, used by another class more than the class on which it is defined.
328
+
329
+ **Solution**: Create a new field in the target class, and change all its users.
330
+
331
+ ---
332
+
333
+ ### Extract Class
334
+
335
+ **Problem**: One class does work that should be done by two.
336
+
337
+ **Solution**: Create a new class and move the relevant fields and methods from the old class into the new class.
338
+
339
+ ```java
340
+ // Before
341
+ class Person {
342
+ private String name;
343
+ private String officeAreaCode;
344
+ private String officeNumber;
345
+
346
+ String getTelephoneNumber() {
347
+ return "(" + officeAreaCode + ") " + officeNumber;
348
+ }
349
+ }
350
+
351
+ // After
352
+ class Person {
353
+ private String name;
354
+ private TelephoneNumber officeTelephone = new TelephoneNumber();
355
+
356
+ String getTelephoneNumber() {
357
+ return officeTelephone.getTelephoneNumber();
358
+ }
359
+ }
360
+
361
+ class TelephoneNumber {
362
+ private String areaCode;
363
+ private String number;
364
+
365
+ String getTelephoneNumber() {
366
+ return "(" + areaCode + ") " + number;
367
+ }
368
+ }
369
+ ```
370
+
371
+ ---
372
+
373
+ ### Inline Class
374
+
375
+ **Problem**: A class isn't doing very much.
376
+
377
+ **Solution**: Move all its features into another class and delete it.
378
+
379
+ ---
380
+
381
+ ### Hide Delegate
382
+
383
+ **Problem**: A client is calling a delegate class of an object through an accessor.
384
+
385
+ **Solution**: Create methods on the server to hide the delegate.
386
+
387
+ ```java
388
+ // Before
389
+ class Person {
390
+ Department department;
391
+
392
+ Department getDepartment() {
393
+ return department;
394
+ }
395
+ }
396
+
397
+ // Client code
398
+ manager = john.getDepartment().getManager();
399
+
400
+ // After
401
+ class Person {
402
+ Department department;
403
+
404
+ Person getManager() {
405
+ return department.getManager();
406
+ }
407
+ }
408
+
409
+ // Client code
410
+ manager = john.getManager();
411
+ ```
412
+
413
+ ---
414
+
415
+ ### Remove Middle Man
416
+
417
+ **Problem**: A class is doing too much simple delegation.
418
+
419
+ **Solution**: Get the client to call the delegate directly.
420
+
421
+ ---
422
+
423
+ ### Introduce Foreign Method
424
+
425
+ **Problem**: A server class you are using needs an additional method, but you can't modify the class.
426
+
427
+ **Solution**: Create a method in the client class with an instance of the server class as its first argument.
428
+
429
+ ```java
430
+ // Before
431
+ Date newStart = new Date(previousEnd.getYear(),
432
+ previousEnd.getMonth(), previousEnd.getDate() + 1);
433
+
434
+ // After
435
+ Date newStart = nextDay(previousEnd);
436
+
437
+ private static Date nextDay(Date arg) {
438
+ return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
439
+ }
440
+ ```
441
+
442
+ ---
443
+
444
+ ### Introduce Local Extension
445
+
446
+ **Problem**: A server class you are using needs several additional methods, but you can't modify the class.
447
+
448
+ **Solution**: Create a new class containing these extra methods. Make this extension class a subclass or wrapper of the original.
449
+
450
+ ---
451
+
452
+ ## 3. Organizing Data
453
+
454
+ ### Self Encapsulate Field
455
+
456
+ **Problem**: You are accessing a field directly, but the coupling to the field is becoming awkward.
457
+
458
+ **Solution**: Create getting and setting methods for the field and use only those to access the field.
459
+
460
+ ```java
461
+ // Before
462
+ private int low, high;
463
+ boolean includes(int arg) {
464
+ return arg >= low && arg <= high;
465
+ }
466
+
467
+ // After
468
+ private int low, high;
469
+ boolean includes(int arg) {
470
+ return arg >= getLow() && arg <= getHigh();
471
+ }
472
+ int getLow() { return low; }
473
+ int getHigh() { return high; }
474
+ ```
475
+
476
+ ---
477
+
478
+ ### Replace Data Value with Object
479
+
480
+ **Problem**: You have a data item that needs additional data or behavior.
481
+
482
+ **Solution**: Turn the data item into an object.
483
+
484
+ ```java
485
+ // Before
486
+ class Order {
487
+ private String customer;
488
+ }
489
+
490
+ // After
491
+ class Order {
492
+ private Customer customer;
493
+ }
494
+
495
+ class Customer {
496
+ private String name;
497
+ }
498
+ ```
499
+
500
+ ---
501
+
502
+ ### Change Value to Reference
503
+
504
+ **Problem**: You have a class with many equal instances that you want to replace with a single object.
505
+
506
+ **Solution**: Turn the object into a reference object.
507
+
508
+ ---
509
+
510
+ ### Change Reference to Value
511
+
512
+ **Problem**: You have a reference object that is small, immutable, and awkward to manage.
513
+
514
+ **Solution**: Turn the reference object into a value object.
515
+
516
+ ---
517
+
518
+ ### Replace Array with Object
519
+
520
+ **Problem**: You have an array in which certain elements mean different things.
521
+
522
+ **Solution**: Replace the array with an object that has a field for each element.
523
+
524
+ ```java
525
+ // Before
526
+ String[] row = new String[3];
527
+ row[0] = "Liverpool";
528
+ row[1] = "15";
529
+
530
+ // After
531
+ Performance row = new Performance();
532
+ row.setName("Liverpool");
533
+ row.setWins("15");
534
+ ```
535
+
536
+ ---
537
+
538
+ ### Duplicate Observed Data
539
+
540
+ **Problem**: You have domain data that only needs to be in a GUI control, but domain methods need access to it.
541
+
542
+ **Solution**: Copy the data to a domain object and set up an observer to synchronize the two.
543
+
544
+ ---
545
+
546
+ ### Change Unidirectional Association to Bidirectional
547
+
548
+ **Problem**: You have two classes that need to use each other's features, but there is only a one-way link.
549
+
550
+ **Solution**: Add back pointers, and change modifiers to update both sets.
551
+
552
+ ---
553
+
554
+ ### Change Bidirectional Association to Unidirectional
555
+
556
+ **Problem**: You have a bidirectional association between two classes, but one of the classes no longer needs features from the other.
557
+
558
+ **Solution**: Drop the unnecessary end of the association.
559
+
560
+ ---
561
+
562
+ ### Replace Magic Number with Symbolic Constant
563
+
564
+ **Problem**: You have a literal number with a particular meaning.
565
+
566
+ **Solution**: Create a constant, name it after the meaning, and replace the number with it.
567
+
568
+ ```java
569
+ // Before
570
+ double potentialEnergy(double mass, double height) {
571
+ return mass * height * 9.81;
572
+ }
573
+
574
+ // After
575
+ static final double GRAVITATIONAL_CONSTANT = 9.81;
576
+
577
+ double potentialEnergy(double mass, double height) {
578
+ return mass * height * GRAVITATIONAL_CONSTANT;
579
+ }
580
+ ```
581
+
582
+ ---
583
+
584
+ ### Encapsulate Field
585
+
586
+ **Problem**: There is a public field.
587
+
588
+ **Solution**: Make it private and provide accessors.
589
+
590
+ ```java
591
+ // Before
592
+ public String name;
593
+
594
+ // After
595
+ private String name;
596
+ public String getName() { return name; }
597
+ public void setName(String name) { this.name = name; }
598
+ ```
599
+
600
+ ---
601
+
602
+ ### Encapsulate Collection
603
+
604
+ **Problem**: A method returns a collection that is a member of the class.
605
+
606
+ **Solution**: Have it return a read-only view and provide add/remove methods.
607
+
608
+ ---
609
+
610
+ ## 4. Simplifying Conditional Expressions
611
+
612
+ ### Decompose Conditional
613
+
614
+ **Problem**: You have a complicated conditional (if-then-else) statement.
615
+
616
+ **Solution**: Extract methods from the condition, then part, and else part.
617
+
618
+ ```java
619
+ // Before
620
+ if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
621
+ charge = quantity * winterRate + winterServiceCharge;
622
+ } else {
623
+ charge = quantity * summerRate;
624
+ }
625
+
626
+ // After
627
+ if (isSummer(date)) {
628
+ charge = summerCharge(quantity);
629
+ } else {
630
+ charge = winterCharge(quantity);
631
+ }
632
+ ```
633
+
634
+ ---
635
+
636
+ ### Consolidate Conditional Expression
637
+
638
+ **Problem**: You have a sequence of conditional tests with the same result.
639
+
640
+ **Solution**: Combine them into a single conditional expression and extract it.
641
+
642
+ ```java
643
+ // Before
644
+ double disabilityAmount() {
645
+ if (seniority < 2) return 0;
646
+ if (monthsDisabled > 12) return 0;
647
+ if (isPartTime) return 0;
648
+ // compute the disability amount
649
+ }
650
+
651
+ // After
652
+ double disabilityAmount() {
653
+ if (isNotEligibleForDisability()) return 0;
654
+ // compute the disability amount
655
+ }
656
+
657
+ boolean isNotEligibleForDisability() {
658
+ return seniority < 2 || monthsDisabled > 12 || isPartTime;
659
+ }
660
+ ```
661
+
662
+ ---
663
+
664
+ ### Consolidate Duplicate Conditional Fragments
665
+
666
+ **Problem**: The same fragment of code is in all branches of a conditional expression.
667
+
668
+ **Solution**: Move it outside of the expression.
669
+
670
+ ```java
671
+ // Before
672
+ if (isSpecialDeal()) {
673
+ total = price * 0.95;
674
+ send();
675
+ } else {
676
+ total = price * 0.98;
677
+ send();
678
+ }
679
+
680
+ // After
681
+ if (isSpecialDeal()) {
682
+ total = price * 0.95;
683
+ } else {
684
+ total = price * 0.98;
685
+ }
686
+ send();
687
+ ```
688
+
689
+ ---
690
+
691
+ ### Remove Control Flag
692
+
693
+ **Problem**: You have a variable that is acting as a control flag for a series of boolean expressions.
694
+
695
+ **Solution**: Use a break or return instead.
696
+
697
+ ---
698
+
699
+ ### Replace Nested Conditional with Guard Clauses
700
+
701
+ **Problem**: A method has conditional behavior that does not make clear the normal path of execution.
702
+
703
+ **Solution**: Use guard clauses for all the special cases.
704
+
705
+ ```java
706
+ // Before
707
+ double getPayAmount() {
708
+ double result;
709
+ if (isDead) {
710
+ result = deadAmount();
711
+ } else {
712
+ if (isSeparated) {
713
+ result = separatedAmount();
714
+ } else {
715
+ if (isRetired) {
716
+ result = retiredAmount();
717
+ } else {
718
+ result = normalPayAmount();
719
+ }
720
+ }
721
+ }
722
+ return result;
723
+ }
724
+
725
+ // After
726
+ double getPayAmount() {
727
+ if (isDead) return deadAmount();
728
+ if (isSeparated) return separatedAmount();
729
+ if (isRetired) return retiredAmount();
730
+ return normalPayAmount();
731
+ }
732
+ ```
733
+
734
+ ---
735
+
736
+ ### Replace Conditional with Polymorphism
737
+
738
+ **Problem**: You have a conditional that chooses different behavior depending on the type of an object.
739
+
740
+ **Solution**: Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract.
741
+
742
+ ```java
743
+ // Before
744
+ double getSpeed() {
745
+ switch (type) {
746
+ case EUROPEAN:
747
+ return getBaseSpeed();
748
+ case AFRICAN:
749
+ return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
750
+ case NORWEGIAN_BLUE:
751
+ return isNailed ? 0 : getBaseSpeed(voltage);
752
+ }
753
+ }
754
+
755
+ // After
756
+ abstract class Bird {
757
+ abstract double getSpeed();
758
+ }
759
+
760
+ class EuropeanBird extends Bird {
761
+ double getSpeed() { return getBaseSpeed(); }
762
+ }
763
+
764
+ class AfricanBird extends Bird {
765
+ double getSpeed() { return getBaseSpeed() - getLoadFactor() * numberOfCoconuts; }
766
+ }
767
+ ```
768
+
769
+ ---
770
+
771
+ ### Introduce Null Object
772
+
773
+ **Problem**: You have repeated checks for a null value.
774
+
775
+ **Solution**: Replace the null value with a null object.
776
+
777
+ ```java
778
+ // Before
779
+ if (customer != null) {
780
+ plan = customer.getPlan();
781
+ } else {
782
+ plan = Plan.NULL;
783
+ }
784
+
785
+ // After
786
+ interface Customer {
787
+ Plan getPlan();
788
+ }
789
+
790
+ class NullCustomer implements Customer {
791
+ Plan getPlan() { return Plan.NULL; }
792
+ }
793
+
794
+ // Usage
795
+ plan = customer.getPlan(); // No null check needed
796
+ ```
797
+
798
+ ---
799
+
800
+ ### Introduce Assertion
801
+
802
+ **Problem**: A section of code assumes something about the state of the program.
803
+
804
+ **Solution**: Make the assumption explicit with an assertion.
805
+
806
+ ```java
807
+ // Before
808
+ double getExpenseLimit() {
809
+ // should have either expense limit or a primary project
810
+ return (expenseLimit != NULL_EXPENSE) ?
811
+ expenseLimit : primaryProject.getMemberExpenseLimit();
812
+ }
813
+
814
+ // After
815
+ double getExpenseLimit() {
816
+ Assert.isTrue(expenseLimit != NULL_EXPENSE || primaryProject != null);
817
+ return (expenseLimit != NULL_EXPENSE) ?
818
+ expenseLimit : primaryProject.getMemberExpenseLimit();
819
+ }
820
+ ```
821
+
822
+ ---
823
+
824
+ ## 5. Simplifying Method Calls
825
+
826
+ ### Rename Method
827
+
828
+ **Problem**: The name of a method does not reveal its purpose.
829
+
830
+ **Solution**: Rename the method.
831
+
832
+ ```java
833
+ // Before
834
+ String getNm() { return name; }
835
+
836
+ // After
837
+ String getName() { return name; }
838
+ ```
839
+
840
+ ---
841
+
842
+ ### Add Parameter
843
+
844
+ **Problem**: A method needs more information from its caller.
845
+
846
+ **Solution**: Add a parameter for an object that can pass on this information.
847
+
848
+ ---
849
+
850
+ ### Remove Parameter
851
+
852
+ **Problem**: A parameter is no longer used by the method body.
853
+
854
+ **Solution**: Remove it.
855
+
856
+ ---
857
+
858
+ ### Separate Query from Modifier
859
+
860
+ **Problem**: You have a method that returns a value but also changes the state of an object.
861
+
862
+ **Solution**: Create two methods, one for the query and one for the modification.
863
+
864
+ ```java
865
+ // Before
866
+ String getNewMissions() {
867
+ String result = "";
868
+ for (Mission mission : missions) {
869
+ if (mission.isNew()) {
870
+ result += mission.getName() + "\n";
871
+ mission.markAsRead();
872
+ }
873
+ }
874
+ return result;
875
+ }
876
+
877
+ // After
878
+ String getNewMissions() {
879
+ return missions.stream()
880
+ .filter(Mission::isNew)
881
+ .map(Mission::getName)
882
+ .collect(Collectors.joining("\n"));
883
+ }
884
+
885
+ void markNewMissionsAsRead() {
886
+ missions.stream()
887
+ .filter(Mission::isNew)
888
+ .forEach(Mission::markAsRead);
889
+ }
890
+ ```
891
+
892
+ ---
893
+
894
+ ### Parameterize Method
895
+
896
+ **Problem**: Several methods do similar things but with different values contained in the method body.
897
+
898
+ **Solution**: Create a single method that uses a parameter for the different values.
899
+
900
+ ```java
901
+ // Before
902
+ void tenPercentRaise() { salary *= 1.1; }
903
+ void fivePercentRaise() { salary *= 1.05; }
904
+
905
+ // After
906
+ void raise(double factor) { salary *= (1 + factor); }
907
+ ```
908
+
909
+ ---
910
+
911
+ ### Replace Parameter with Explicit Methods
912
+
913
+ **Problem**: You have a method that runs different code depending on the values of an enumerated parameter.
914
+
915
+ **Solution**: Create a separate method for each value of the parameter.
916
+
917
+ ```java
918
+ // Before
919
+ void setValue(String name, int value) {
920
+ if (name.equals("height")) {
921
+ height = value;
922
+ return;
923
+ }
924
+ if (name.equals("width")) {
925
+ width = value;
926
+ return;
927
+ }
928
+ }
929
+
930
+ // After
931
+ void setHeight(int arg) { height = arg; }
932
+ void setWidth(int arg) { width = arg; }
933
+ ```
934
+
935
+ ---
936
+
937
+ ### Preserve Whole Object
938
+
939
+ **Problem**: You are getting several values from an object and passing these values as parameters in a method call.
940
+
941
+ **Solution**: Instead, pass the whole object.
942
+
943
+ ```java
944
+ // Before
945
+ int low = daysTempRange().getLow();
946
+ int high = daysTempRange().getHigh();
947
+ boolean withinPlan = plan.withinRange(low, high);
948
+
949
+ // After
950
+ boolean withinPlan = plan.withinRange(daysTempRange());
951
+ ```
952
+
953
+ ---
954
+
955
+ ### Replace Parameter with Method
956
+
957
+ **Problem**: An object invokes a method, then passes the result as a parameter for a method. The receiver can also invoke this method.
958
+
959
+ **Solution**: Remove the parameter and let the receiver invoke the method.
960
+
961
+ ```java
962
+ // Before
963
+ int basePrice = quantity * itemPrice;
964
+ discountLevel = getDiscountLevel();
965
+ double finalPrice = discountedPrice(basePrice, discountLevel);
966
+
967
+ // After
968
+ double finalPrice = discountedPrice();
969
+ ```
970
+
971
+ ---
972
+
973
+ ### Introduce Parameter Object
974
+
975
+ **Problem**: You have a group of parameters that naturally go together.
976
+
977
+ **Solution**: Replace them with an object.
978
+
979
+ ```java
980
+ // Before
981
+ void printReport(Date start, Date end, String title, String author);
982
+
983
+ // After
984
+ class ReportParameters {
985
+ Date start;
986
+ Date end;
987
+ String title;
988
+ String author;
989
+ }
990
+
991
+ void printReport(ReportParameters params);
992
+ ```
993
+
994
+ ---
995
+
996
+ ### Remove Setting Method
997
+
998
+ **Problem**: The value of a field should be set only when it is initialized, and not changed at any other time.
999
+
1000
+ **Solution**: Remove any setting method for that field.
1001
+
1002
+ ---
1003
+
1004
+ ### Hide Method
1005
+
1006
+ **Problem**: A method is not used by any other class.
1007
+
1008
+ **Solution**: Make the method private.
1009
+
1010
+ ---
1011
+
1012
+ ### Replace Constructor with Factory Method
1013
+
1014
+ **Problem**: You want to do more than simple construction when you create an object.
1015
+
1016
+ **Solution**: Replace the constructor with a factory method.
1017
+
1018
+ ```java
1019
+ // Before
1020
+ Employee(int type) {
1021
+ this.type = type;
1022
+ }
1023
+
1024
+ // After
1025
+ static Employee create(int type) {
1026
+ switch (type) {
1027
+ case ENGINEER: return new Engineer();
1028
+ case SALESMAN: return new Salesman();
1029
+ case MANAGER: return new Manager();
1030
+ }
1031
+ }
1032
+ ```
1033
+
1034
+ ---
1035
+
1036
+ ### Encapsulate Downcast
1037
+
1038
+ **Problem**: A method returns an object that needs to be downcasted by its callers.
1039
+
1040
+ **Solution**: Move the downcast to within the method.
1041
+
1042
+ ```java
1043
+ // Before
1044
+ Object lastReading() {
1045
+ return readings.lastElement();
1046
+ }
1047
+
1048
+ // After
1049
+ Reading lastReading() {
1050
+ return (Reading) readings.lastElement();
1051
+ }
1052
+ ```
1053
+
1054
+ ---
1055
+
1056
+ ### Replace Error Code with Exception
1057
+
1058
+ **Problem**: A method returns a special code to indicate an error.
1059
+
1060
+ **Solution**: Throw an exception instead.
1061
+
1062
+ ```java
1063
+ // Before
1064
+ int withdraw(int amount) {
1065
+ if (amount > balance) {
1066
+ return -1;
1067
+ }
1068
+ balance -= amount;
1069
+ return 0;
1070
+ }
1071
+
1072
+ // After
1073
+ void withdraw(int amount) throws BalanceException {
1074
+ if (amount > balance) {
1075
+ throw new BalanceException();
1076
+ }
1077
+ balance -= amount;
1078
+ }
1079
+ ```
1080
+
1081
+ ---
1082
+
1083
+ ### Replace Exception with Test
1084
+
1085
+ **Problem**: You are throwing an exception on a condition the caller could have checked first.
1086
+
1087
+ **Solution**: Change the caller to make the test first.
1088
+
1089
+ ```java
1090
+ // Before
1091
+ double getValueForPeriod(int periodNumber) {
1092
+ try {
1093
+ return values[periodNumber];
1094
+ } catch (ArrayIndexOutOfBoundsException e) {
1095
+ return 0;
1096
+ }
1097
+ }
1098
+
1099
+ // After
1100
+ double getValueForPeriod(int periodNumber) {
1101
+ if (periodNumber >= values.length) {
1102
+ return 0;
1103
+ }
1104
+ return values[periodNumber];
1105
+ }
1106
+ ```
1107
+
1108
+ ---
1109
+
1110
+ ## 6. Dealing with Generalization
1111
+
1112
+ ### Pull Up Field
1113
+
1114
+ **Problem**: Two subclasses have the same field.
1115
+
1116
+ **Solution**: Move the field to the superclass.
1117
+
1118
+ ---
1119
+
1120
+ ### Pull Up Method
1121
+
1122
+ **Problem**: You have methods with identical results on subclasses.
1123
+
1124
+ **Solution**: Move them to the superclass.
1125
+
1126
+ ---
1127
+
1128
+ ### Pull Up Constructor Body
1129
+
1130
+ **Problem**: You have constructors on subclasses with mostly identical bodies.
1131
+
1132
+ **Solution**: Create a superclass constructor; call this from the subclass methods.
1133
+
1134
+ ---
1135
+
1136
+ ### Push Down Method
1137
+
1138
+ **Problem**: Behavior on a superclass is relevant only for some of its subclasses.
1139
+
1140
+ **Solution**: Move it to those subclasses.
1141
+
1142
+ ---
1143
+
1144
+ ### Push Down Field
1145
+
1146
+ **Problem**: A field is used only by some subclasses.
1147
+
1148
+ **Solution**: Move it to those subclasses.
1149
+
1150
+ ---
1151
+
1152
+ ### Extract Subclass
1153
+
1154
+ **Problem**: A class has features that are used only in some instances.
1155
+
1156
+ **Solution**: Create a subclass for that subset of features.
1157
+
1158
+ ```java
1159
+ // Before
1160
+ class JobItem {
1161
+ private int unitPrice;
1162
+ private boolean isLabor;
1163
+
1164
+ int getTotalPrice() {
1165
+ return isLabor ? getQuantity() * getEmployee().getRate()
1166
+ : unitPrice * getQuantity();
1167
+ }
1168
+ }
1169
+
1170
+ // After
1171
+ abstract class JobItem {
1172
+ abstract int getTotalPrice();
1173
+ }
1174
+
1175
+ class LaborItem extends JobItem {
1176
+ int getTotalPrice() {
1177
+ return getQuantity() * getEmployee().getRate();
1178
+ }
1179
+ }
1180
+
1181
+ class PartsItem extends JobItem {
1182
+ int getTotalPrice() {
1183
+ return unitPrice * getQuantity();
1184
+ }
1185
+ }
1186
+ ```
1187
+
1188
+ ---
1189
+
1190
+ ### Extract Superclass
1191
+
1192
+ **Problem**: You have two classes with similar features.
1193
+
1194
+ **Solution**: Create a superclass and move the common features to the superclass.
1195
+
1196
+ ---
1197
+
1198
+ ### Extract Interface
1199
+
1200
+ **Problem**: Several clients use the same subset of a class interface, or two classes have part of their interfaces in common.
1201
+
1202
+ **Solution**: Extract the subset into an interface.
1203
+
1204
+ ```java
1205
+ // Before
1206
+ class Employee {
1207
+ double getRate();
1208
+ boolean hasSpecialSkill();
1209
+ }
1210
+
1211
+ // After
1212
+ interface Billable {
1213
+ double getRate();
1214
+ }
1215
+
1216
+ class Employee implements Billable {
1217
+ public double getRate() { ... }
1218
+ boolean hasSpecialSkill() { ... }
1219
+ }
1220
+ ```
1221
+
1222
+ ---
1223
+
1224
+ ### Collapse Hierarchy
1225
+
1226
+ **Problem**: A superclass and subclass are not very different.
1227
+
1228
+ **Solution**: Merge them together.
1229
+
1230
+ ---
1231
+
1232
+ ### Form Template Method
1233
+
1234
+ **Problem**: You have two methods in subclasses that carry out similar steps in the same order, but the steps are different.
1235
+
1236
+ **Solution**: Get the steps into methods with the same signature, so that the original methods become the same. Then you can pull them up.
1237
+
1238
+ ---
1239
+
1240
+ ### Replace Inheritance with Delegation
1241
+
1242
+ **Problem**: A subclass uses only part of a superclass interface or does not want to inherit data.
1243
+
1244
+ **Solution**: Create a field for the superclass, adjust methods to delegate to the superclass, and remove the inheritance.
1245
+
1246
+ ---
1247
+
1248
+ ### Replace Delegation with Inheritance
1249
+
1250
+ **Problem**: You're using delegation and are often writing many simple delegations for the entire interface.
1251
+
1252
+ **Solution**: Make the delegating class a subclass of the delegate.
1253
+
1254
+ ---
1255
+
1256
+ ## Summary: Refactoring Quick Reference Card
1257
+
1258
+ | Smell | Primary Refactoring |
1259
+ |-------|---------------------|
1260
+ | Duplicated Code | Extract Method |
1261
+ | Long Method | Extract Method |
1262
+ | Large Class | Extract Class |
1263
+ | Long Parameter List | Introduce Parameter Object |
1264
+ | Divergent Change | Extract Class |
1265
+ | Shotgun Surgery | Move Method |
1266
+ | Feature Envy | Move Method |
1267
+ | Data Clumps | Extract Class |
1268
+ | Primitive Obsession | Replace Data Value with Object |
1269
+ | Switch Statements | Replace Conditional with Polymorphism |
1270
+ | Parallel Inheritance | Move Method |
1271
+ | Lazy Class | Inline Class |
1272
+ | Speculative Generality | Inline Class |
1273
+ | Temporary Field | Extract Class |
1274
+ | Message Chains | Hide Delegate |
1275
+ | Middle Man | Remove Middle Man |
1276
+ | Inappropriate Intimacy | Move Method |
1277
+ | Alternative Classes | Rename Method |
1278
+ | Incomplete Library Class | Introduce Foreign Method |
1279
+ | Data Class | Encapsulate Collection |
1280
+ | Refused Bequest | Push Down Method |
1281
+ | Comments | Extract Method |