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 AbstractNews 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 AbstractNews, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
41 | abstract class AbstractNews extends Content implements |
||
42 | CategorizableInterface, |
||
43 | MetatagInterface, |
||
44 | NewsInterface, |
||
45 | PublishableInterface, |
||
46 | RoutableInterface, |
||
47 | SearchableInterface, |
||
48 | TemplateableInterface |
||
49 | { |
||
50 | use CategorizableTrait; |
||
51 | use PublishableTrait; |
||
52 | use MetatagTrait; |
||
53 | use RoutableTrait; |
||
54 | use SearchableTrait; |
||
55 | use TemplateableTrait; |
||
56 | |||
57 | /** |
||
58 | * @var Translation|string|null |
||
59 | */ |
||
60 | private $title; |
||
61 | |||
62 | /** |
||
63 | * @var Translation|string|null |
||
64 | */ |
||
65 | private $subtitle; |
||
66 | |||
67 | /** |
||
68 | * @var Translation|string|null |
||
69 | */ |
||
70 | private $summary; |
||
71 | |||
72 | /** |
||
73 | * @var Translation|string|null |
||
74 | */ |
||
75 | private $content; |
||
76 | |||
77 | /** |
||
78 | * @var Translation|string|null |
||
79 | */ |
||
80 | private $image; |
||
81 | |||
82 | /** |
||
83 | * @var DateTimeInterface|null |
||
84 | */ |
||
85 | private $newsDate; |
||
86 | |||
87 | /** |
||
88 | * @var Translation|string|null |
||
89 | */ |
||
90 | private $infoUrl; |
||
91 | |||
92 | /** |
||
93 | * @var array |
||
94 | */ |
||
95 | protected $keywords; |
||
96 | |||
97 | // ========================================================================== |
||
98 | // INIT |
||
99 | // ========================================================================== |
||
100 | |||
101 | /** |
||
102 | * Section constructor. |
||
103 | * @param array $data The data. |
||
104 | */ |
||
105 | View Code Duplication | public function __construct(array $data = null) |
|
113 | |||
114 | // ========================================================================== |
||
115 | // FUNCTIONS |
||
116 | // ========================================================================== |
||
117 | |||
118 | /** |
||
119 | * In the datetime attribute of the <time> tag |
||
120 | * @return string The datetime attribute formatted. |
||
121 | */ |
||
122 | public function dateTimeDate() |
||
128 | |||
129 | /** |
||
130 | * Some dates cannot be null |
||
131 | * @return void |
||
132 | */ |
||
133 | public function verifyDates() |
||
143 | |||
144 | /** |
||
145 | * @return string The date filtered for admin dual select input and others. |
||
146 | */ |
||
147 | public function adminDateFilter() |
||
151 | |||
152 | // ========================================================================== |
||
153 | // SETTERS |
||
154 | // ========================================================================== |
||
155 | |||
156 | /** |
||
157 | * @param mixed $title The news title (localized). |
||
158 | * @return self |
||
159 | */ |
||
160 | public function setTitle($title) |
||
166 | |||
167 | /** |
||
168 | * @param mixed $subtitle The news subtitle (localized). |
||
169 | * @return self |
||
170 | */ |
||
171 | public function setSubtitle($subtitle) |
||
177 | |||
178 | /** |
||
179 | * @param mixed $summary The news summary (localized). |
||
180 | * @return self |
||
181 | */ |
||
182 | public function setSummary($summary) |
||
188 | |||
189 | /** |
||
190 | * @param mixed $content The news content (localized). |
||
191 | * @return self |
||
192 | */ |
||
193 | public function setContent($content) |
||
199 | |||
200 | /** |
||
201 | * @param mixed $image The section main image (localized). |
||
202 | * @return self |
||
203 | */ |
||
204 | public function setImage($image) |
||
210 | |||
211 | /** |
||
212 | * @param mixed $url The info URL (news source or where to find more information; localized). |
||
213 | * @return self |
||
214 | */ |
||
215 | public function setInfoUrl($url) |
||
221 | |||
222 | /** |
||
223 | * @param string|DateTimeInterface $newsDate The news date. |
||
224 | * @throws InvalidArgumentException If the timestamp is invalid. |
||
225 | * @return self |
||
226 | */ |
||
227 | View Code Duplication | public function setNewsDate($newsDate) |
|
246 | |||
247 | /** |
||
248 | * Set the object's keywords. |
||
249 | * |
||
250 | * @param string|string[] $keywords One or more entries. |
||
251 | * @return self |
||
252 | */ |
||
253 | public function setKeywords($keywords) |
||
259 | |||
260 | // ========================================================================== |
||
261 | // GETTERS |
||
262 | // ========================================================================== |
||
263 | |||
264 | /** |
||
265 | * @return Translation|string|null |
||
266 | */ |
||
267 | public function title() |
||
271 | |||
272 | /** |
||
273 | * @return Translation|string|null |
||
274 | */ |
||
275 | public function subtitle() |
||
279 | |||
280 | /** |
||
281 | * @return Translation|string|null |
||
282 | */ |
||
283 | public function summary() |
||
287 | |||
288 | /** |
||
289 | * @return Translation|string|null |
||
290 | */ |
||
291 | public function infoUrl() |
||
295 | |||
296 | /** |
||
297 | * @return DateTimeInterface|null |
||
298 | */ |
||
299 | public function newsDate() |
||
303 | |||
304 | /** |
||
305 | * @return Translation|string|null |
||
306 | */ |
||
307 | public function content() |
||
311 | |||
312 | /** |
||
313 | * @return Translation|string|null |
||
314 | */ |
||
315 | public function image() |
||
319 | |||
320 | // ========================================================================== |
||
321 | // META TAGS |
||
322 | // ========================================================================== |
||
323 | |||
324 | /** |
||
325 | * MetatagTrait > canonical_url |
||
326 | * |
||
327 | * @return string |
||
328 | * @todo |
||
329 | */ |
||
330 | public function canonicalUrl() |
||
334 | |||
335 | /** |
||
336 | * @return Translation|string|null |
||
337 | */ |
||
338 | public function defaultMetaTitle() |
||
342 | |||
343 | /** |
||
344 | * @return Translation|string|null |
||
345 | */ |
||
346 | View Code Duplication | public function defaultMetaDescription() |
|
360 | |||
361 | /** |
||
362 | * @return Translation|string|null |
||
363 | */ |
||
364 | public function defaultMetaImage() |
||
368 | |||
369 | /** |
||
370 | * Retrieve the object's keywords. |
||
371 | * |
||
372 | * @return string[] |
||
373 | */ |
||
374 | public function keywords() |
||
378 | |||
379 | // ========================================================================== |
||
380 | // Utils |
||
381 | // ========================================================================== |
||
382 | |||
383 | /** |
||
384 | * Parse the property value as a "multiple" value type. |
||
385 | * |
||
386 | * @param mixed $value The value being converted to an array. |
||
387 | * @param string|PropertyInterface $separator The boundary string. |
||
388 | * @return array |
||
389 | */ |
||
390 | View Code Duplication | public function parseAsMultiple($value, $separator = ',') |
|
419 | |||
420 | // ========================================================================== |
||
421 | // EVENTS |
||
422 | // ========================================================================== |
||
423 | |||
424 | /** |
||
425 | * {@inheritdoc} |
||
426 | * |
||
427 | * @return boolean |
||
428 | */ |
||
429 | public function preSave() |
||
437 | |||
438 | /** |
||
439 | * {@inheritdoc} |
||
440 | * |
||
441 | * @param array $properties Optional properties to update. |
||
442 | * @return boolean |
||
443 | */ |
||
444 | public function preUpdate(array $properties = null) |
||
452 | |||
453 | /** |
||
454 | * @return boolean Parent postSave(). |
||
455 | */ |
||
456 | public function postSave() |
||
463 | |||
464 | /** |
||
465 | * @param array|null $properties Properties. |
||
466 | * @return boolean |
||
467 | */ |
||
468 | public function postUpdate(array $properties = null) |
||
475 | |||
476 | /** |
||
477 | * GenericRoute checks if the route is active. |
||
478 | * Default in RoutableTrait. |
||
479 | * |
||
480 | * @return boolean |
||
481 | */ |
||
482 | public function isActiveRoute() |
||
489 | } |
||
490 |
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.