Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like SearchLog often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use SearchLog, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | class SearchLog extends AbstractModel implements SearchLogInterface |
||
17 | { |
||
18 | /** |
||
19 | * The search identifier this specific search log belongs to. |
||
20 | * |
||
21 | * @var string |
||
22 | */ |
||
23 | private $searchIdent; |
||
24 | |||
25 | /** |
||
26 | * The searched keyword. |
||
27 | * |
||
28 | * @var string |
||
29 | */ |
||
30 | private $keyword; |
||
31 | |||
32 | /** |
||
33 | * The search options, if defined. |
||
34 | * |
||
35 | * @var array|null |
||
36 | */ |
||
37 | private $options; |
||
38 | |||
39 | /** |
||
40 | * Number of search results. |
||
41 | * |
||
42 | * @var integer|null |
||
43 | */ |
||
44 | private $numResults; |
||
45 | |||
46 | /** |
||
47 | * Detailed results, if available. |
||
48 | * |
||
49 | * @var array|null |
||
50 | */ |
||
51 | private $results; |
||
52 | |||
53 | /** |
||
54 | * Client session ID |
||
55 | * |
||
56 | * @var string|null |
||
57 | */ |
||
58 | private $sessionId; |
||
59 | |||
60 | /** |
||
61 | * Client IP address of the end-user. |
||
62 | * |
||
63 | * @var integer|null |
||
64 | */ |
||
65 | private $ip; |
||
66 | |||
67 | /** |
||
68 | * Language of the end-user or source URI. |
||
69 | * |
||
70 | * @var string|null |
||
71 | */ |
||
72 | private $lang; |
||
73 | |||
74 | /** |
||
75 | * The search origin; an identifier representing where the search was executed from. |
||
76 | * |
||
77 | * @var string|null |
||
78 | */ |
||
79 | private $origin; |
||
80 | |||
81 | /** |
||
82 | * Timestamp of the search request. |
||
83 | * |
||
84 | * @var DateTimeInterface|null |
||
85 | */ |
||
86 | private $ts; |
||
87 | |||
88 | /** |
||
89 | * Set the log's associated search identifier. |
||
90 | * |
||
91 | * @param string $ident The search identifier. |
||
92 | * @throws InvalidArgumentException If the identifier is not a string. |
||
93 | * @return SearchLog Chainable |
||
94 | */ |
||
95 | public function setSearchIdent($ident) |
||
107 | |||
108 | /** |
||
109 | * Retrieve the log's associated search identifier. |
||
110 | * |
||
111 | * @return string |
||
112 | */ |
||
113 | public function searchIdent() |
||
117 | |||
118 | /** |
||
119 | * Set the searched term. |
||
120 | * |
||
121 | * @param string $kw The searched term / keyword. |
||
122 | * @throws InvalidArgumentException If the keyword is not a string. |
||
123 | * @return SearchLog Chainable |
||
124 | */ |
||
125 | public function setKeyword($kw) |
||
137 | |||
138 | /** |
||
139 | * Retrieve the searched term. |
||
140 | * |
||
141 | * @return string |
||
142 | */ |
||
143 | public function keyword() |
||
147 | |||
148 | /** |
||
149 | * Set the options applied to the search. |
||
150 | * |
||
151 | * @param mixed $options The search options, if defined. |
||
152 | * @throws InvalidArgumentException If the options is not an array or invalid JSON. |
||
153 | * @return SearchLog Chainable |
||
154 | */ |
||
155 | public function setOptions($options) |
||
176 | |||
177 | /** |
||
178 | * Retrieve the options applied to the search. |
||
179 | * |
||
180 | * @return array |
||
181 | */ |
||
182 | public function options() |
||
186 | |||
187 | /** |
||
188 | * Set the result count. |
||
189 | * |
||
190 | * @param integer $count The number of results from the search. |
||
191 | * @return SearchLog Chainable |
||
192 | */ |
||
193 | public function setNumResults($count) |
||
199 | |||
200 | /** |
||
201 | * Retrieve the result count. |
||
202 | * |
||
203 | * @return integer |
||
204 | */ |
||
205 | public function numResults() |
||
209 | |||
210 | /** |
||
211 | * Set the collection of results. |
||
212 | * |
||
213 | * @param mixed $results The search results data, if available. |
||
214 | * @throws InvalidArgumentException If the results is not an array or invalid JSON. |
||
215 | * @return SearchLog Chainable |
||
216 | */ |
||
217 | public function setResults($results) |
||
240 | |||
241 | /** |
||
242 | * Retrieve the collection of results. |
||
243 | * |
||
244 | * @return array |
||
245 | */ |
||
246 | public function results() |
||
250 | |||
251 | /** |
||
252 | * Set the client session ID. |
||
253 | * |
||
254 | * @param string $id The session identifier. Typically, {@see session_id()}. |
||
255 | * @throws InvalidArgumentException If the session id is not a string. |
||
256 | * @return SearchLog Chainable |
||
257 | */ |
||
258 | public function setSessionId($id) |
||
275 | |||
276 | /** |
||
277 | * Retrieve the client session ID. |
||
278 | * |
||
279 | * @return string |
||
280 | */ |
||
281 | public function sessionId() |
||
285 | |||
286 | /** |
||
287 | * Set the client IP address. |
||
288 | * |
||
289 | * @param integer|null $ip The remote IP at object creation. |
||
290 | * @return SearchLog Chainable |
||
291 | */ |
||
292 | public function setIp($ip) |
||
311 | |||
312 | /** |
||
313 | * Retrieve the client IP address. |
||
314 | * |
||
315 | * @return integer|null |
||
316 | */ |
||
317 | public function ip() |
||
321 | |||
322 | /** |
||
323 | * Set the origin language. |
||
324 | * |
||
325 | * @param string $lang The language code. |
||
326 | * @throws InvalidArgumentException If the argument is not a string. |
||
327 | * @return SearchLog Chainable |
||
328 | */ |
||
329 | View Code Duplication | public function setLang($lang) |
|
343 | |||
344 | /** |
||
345 | * Retrieve the language. |
||
346 | * |
||
347 | * @return string |
||
348 | */ |
||
349 | public function lang() |
||
353 | |||
354 | /** |
||
355 | * Set the origin of the search request. |
||
356 | * |
||
357 | * @param string $origin The source URL or identifier of the submission. |
||
358 | * @throws InvalidArgumentException If the argument is not a string. |
||
359 | * @return SearchLog Chainable |
||
360 | */ |
||
361 | View Code Duplication | public function setOrigin($origin) |
|
375 | |||
376 | /** |
||
377 | * Resolve the origin of the search. |
||
378 | * |
||
379 | * @return string |
||
380 | */ |
||
381 | public function resolveOrigin() |
||
394 | |||
395 | /** |
||
396 | * Retrieve the origin of the search request. |
||
397 | * |
||
398 | * @return string |
||
399 | */ |
||
400 | public function origin() |
||
404 | |||
405 | /** |
||
406 | * Set when the search was initiated. |
||
407 | * |
||
408 | * @param DateTime|string|null $timestamp The timestamp of search request. |
||
409 | * NULL is accepted and instances of DateTimeInterface are recommended; |
||
410 | * any other value will be converted (if possible) into one. |
||
411 | * @throws InvalidArgumentException If the timestamp is invalid. |
||
412 | * @return SearchLog Chainable |
||
413 | */ |
||
414 | public function setTs($timestamp) |
||
441 | |||
442 | /** |
||
443 | * Retrieve the creation timestamp. |
||
444 | * |
||
445 | * @return DateTime|null |
||
446 | */ |
||
447 | public function ts() |
||
451 | |||
452 | /** |
||
453 | * Event called before _creating_ the object. |
||
454 | * |
||
455 | * @see Charcoal\Source\StorableTrait::preSave() For the "create" Event. |
||
456 | * @return boolean |
||
457 | */ |
||
458 | public function preSave() |
||
478 | } |
||
479 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.