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 AbstractSection 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 AbstractSection, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
47 | abstract class AbstractSection extends Content implements |
||
48 | HierarchicalInterface, |
||
49 | MetatagInterface, |
||
50 | RoutableInterface, |
||
51 | SearchableInterface, |
||
52 | SectionInterface, |
||
53 | TemplateableInterface |
||
54 | { |
||
55 | use HierarchicalTrait; |
||
56 | use MetatagTrait; |
||
57 | use RoutableTrait; |
||
58 | use SearchableTrait; |
||
59 | use TemplateableTrait; |
||
60 | |||
61 | const TYPE_BLOCKS = 'charcoal/cms/section/blocks-section'; |
||
62 | const TYPE_CONTENT = 'charcoal/cms/section/content-section'; |
||
63 | const TYPE_EMPTY = 'charcoal/cms/section/empty-section'; |
||
64 | const TYPE_EXTERNAL = 'charcoal/cms/section/external-section'; |
||
65 | const DEFAULT_TYPE = self::TYPE_CONTENT; |
||
66 | |||
67 | /** |
||
68 | * @var string |
||
69 | */ |
||
70 | private $sectionType = self::DEFAULT_TYPE; |
||
71 | |||
72 | /** |
||
73 | * @var Translation|string|null |
||
74 | */ |
||
75 | private $title; |
||
76 | |||
77 | /** |
||
78 | * @var Translation|string|null |
||
79 | */ |
||
80 | private $subtitle; |
||
81 | |||
82 | /** |
||
83 | * @var Translation|string|null |
||
84 | */ |
||
85 | private $content; |
||
86 | |||
87 | /** |
||
88 | * @var Translation|string|null |
||
89 | */ |
||
90 | private $image; |
||
91 | |||
92 | /** |
||
93 | * The menus this object is shown in. |
||
94 | * |
||
95 | * @var string[] |
||
96 | */ |
||
97 | protected $inMenu; |
||
98 | |||
99 | /** |
||
100 | * @var array |
||
101 | */ |
||
102 | protected $keywords; |
||
103 | |||
104 | /** |
||
105 | * @var Translation|string $summary |
||
106 | */ |
||
107 | protected $summary; |
||
108 | |||
109 | /** |
||
110 | * @var string $externalUrl |
||
111 | */ |
||
112 | protected $externalUrl; |
||
113 | |||
114 | /** |
||
115 | * @var boolean $locked |
||
116 | */ |
||
117 | protected $locked; |
||
118 | |||
119 | // ========================================================================== |
||
120 | // INIT |
||
121 | // ========================================================================== |
||
122 | |||
123 | /** |
||
124 | * Section constructor. |
||
125 | * @param array $data Init data. |
||
126 | */ |
||
127 | View Code Duplication | public function __construct(array $data = null) |
|
135 | |||
136 | // ========================================================================== |
||
137 | // FUNCTIONS |
||
138 | // ========================================================================== |
||
139 | |||
140 | /** |
||
141 | * Determine if the object can be deleted. |
||
142 | * |
||
143 | * @return boolean |
||
144 | */ |
||
145 | public function isDeletable() |
||
149 | |||
150 | /** |
||
151 | * Retrieve the object's title. |
||
152 | * |
||
153 | * @return string |
||
154 | */ |
||
155 | public function hierarchicalLabel() |
||
159 | |||
160 | /** |
||
161 | * HierarchicalTrait > loadChildren |
||
162 | * |
||
163 | * @return \ArrayAccess|\Traversable |
||
164 | */ |
||
165 | public function loadChildren() |
||
188 | |||
189 | // ========================================================================== |
||
190 | // SETTERS |
||
191 | // ========================================================================== |
||
192 | |||
193 | /** |
||
194 | * Set the section's type. |
||
195 | * |
||
196 | * @param string $type The section type. |
||
197 | * @throws InvalidArgumentException If the section type is not a string or not a valid section type. |
||
198 | * @return self |
||
199 | */ |
||
200 | public function setSectionType($type) |
||
212 | |||
213 | /** |
||
214 | * Set the menus this object belongs to. |
||
215 | * |
||
216 | * @param string|string[] $menu One or more menu identifiers. |
||
217 | * @return self |
||
218 | */ |
||
219 | public function setInMenu($menu) |
||
225 | |||
226 | /** |
||
227 | * Set the object's keywords. |
||
228 | * |
||
229 | * @param string|string[] $keywords One or more entries. |
||
230 | * @return self |
||
231 | */ |
||
232 | public function setKeywords($keywords) |
||
238 | |||
239 | /** |
||
240 | * @param Translation|string|null $summary The summary. |
||
241 | * @return self |
||
242 | */ |
||
243 | public function setSummary($summary) |
||
249 | |||
250 | /** |
||
251 | * @param Translation|string|null $externalUrl The external url. |
||
252 | * @return self |
||
253 | */ |
||
254 | public function setExternalUrl($externalUrl) |
||
260 | |||
261 | /** |
||
262 | * Section is locked when you can't change the URL |
||
263 | * @param boolean $locked Prevent new route creation about that object. |
||
264 | * @return self |
||
265 | */ |
||
266 | public function setLocked($locked) |
||
272 | |||
273 | /** |
||
274 | * @param Translation|string|null $title The section title (localized). |
||
275 | * @return self |
||
276 | */ |
||
277 | public function setTitle($title) |
||
283 | |||
284 | /** |
||
285 | * @param Translation|string|null $subtitle The section subtitle (localized). |
||
286 | * @return self |
||
287 | */ |
||
288 | public function setSubtitle($subtitle) |
||
294 | |||
295 | /** |
||
296 | * @param Translation|string|null $content The section content (localized). |
||
297 | * @return self |
||
298 | */ |
||
299 | public function setContent($content) |
||
305 | |||
306 | /** |
||
307 | * @param mixed $image The section main image (localized). |
||
308 | * @return self |
||
309 | */ |
||
310 | public function setImage($image) |
||
316 | |||
317 | // ========================================================================== |
||
318 | // GETTERS |
||
319 | // ========================================================================== |
||
320 | |||
321 | /** |
||
322 | * Retrieve the section's type. |
||
323 | * |
||
324 | * @return string |
||
325 | */ |
||
326 | public function sectionType() |
||
330 | |||
331 | /** |
||
332 | * @return Translation|string|null |
||
333 | */ |
||
334 | public function title() |
||
338 | |||
339 | /** |
||
340 | * @return Translation|string|null |
||
341 | */ |
||
342 | public function subtitle() |
||
346 | |||
347 | /** |
||
348 | * @return Translation|string|null |
||
349 | */ |
||
350 | public function content() |
||
354 | |||
355 | /** |
||
356 | * @return Translation|string|null |
||
357 | */ |
||
358 | public function image() |
||
362 | |||
363 | /** |
||
364 | * Retrieve the menus this object belongs to. |
||
365 | * |
||
366 | * @return Translation|string|null |
||
367 | */ |
||
368 | public function inMenu() |
||
372 | |||
373 | /** |
||
374 | * Retrieve the object's keywords. |
||
375 | * |
||
376 | * @return string[] |
||
377 | */ |
||
378 | public function keywords() |
||
382 | |||
383 | /** |
||
384 | * HierarchicalTrait > loadChildren |
||
385 | * |
||
386 | * @return Translation|string|null |
||
387 | */ |
||
388 | public function summary() |
||
392 | |||
393 | /** |
||
394 | * @return Translation|string|null |
||
395 | */ |
||
396 | public function externalUrl() |
||
400 | |||
401 | /** |
||
402 | * @return boolean Or Null. |
||
403 | */ |
||
404 | public function locked() |
||
408 | |||
409 | // ========================================================================== |
||
410 | // DEFAULT META |
||
411 | // ========================================================================== |
||
412 | |||
413 | /** |
||
414 | * MetatagTrait > canonicalUrl |
||
415 | * |
||
416 | * @todo |
||
417 | * @return string |
||
418 | */ |
||
419 | public function canonicalUrl() |
||
423 | |||
424 | /** |
||
425 | * @return Translation|string|null |
||
426 | */ |
||
427 | public function defaultMetaTitle() |
||
431 | |||
432 | /** |
||
433 | * @return Translation|string|null |
||
434 | */ |
||
435 | View Code Duplication | public function defaultMetaDescription() |
|
449 | |||
450 | /** |
||
451 | * @return Translation|string|null |
||
452 | */ |
||
453 | public function defaultMetaImage() |
||
457 | |||
458 | // ========================================================================== |
||
459 | // Utils |
||
460 | // ========================================================================== |
||
461 | |||
462 | /** |
||
463 | * Parse the property value as a "multiple" value type. |
||
464 | * |
||
465 | * @param mixed $value The value being converted to an array. |
||
466 | * @param string|PropertyInterface $separator The boundary string. |
||
467 | * @return array |
||
468 | */ |
||
469 | View Code Duplication | public function parseAsMultiple($value, $separator = ',') |
|
498 | |||
499 | // ========================================================================== |
||
500 | // EVENTS |
||
501 | // ========================================================================== |
||
502 | |||
503 | /** |
||
504 | * Route generated on postSave in case |
||
505 | * it contains the ID of the section, which |
||
506 | * you only get once you have save |
||
507 | * |
||
508 | * @return boolean |
||
509 | */ |
||
510 | public function postSave() |
||
519 | |||
520 | /** |
||
521 | * Check whatever before the update. |
||
522 | * |
||
523 | * @param array|null $properties Properties. |
||
524 | * @return boolean |
||
525 | */ |
||
526 | public function postUpdate(array $properties = null) |
||
534 | |||
535 | /** |
||
536 | * {@inheritdoc} |
||
537 | * |
||
538 | * @return boolean |
||
539 | */ |
||
540 | public function preSave() |
||
548 | |||
549 | /** |
||
550 | * {@inheritdoc} |
||
551 | * |
||
552 | * @param array $properties Optional properties to update. |
||
553 | * @return boolean |
||
554 | */ |
||
555 | public function preUpdate(array $properties = null) |
||
563 | |||
564 | /** |
||
565 | * Event called before _deleting_ the object. |
||
566 | * |
||
567 | * @see \Charcoal\Model\AbstractModel::preDelete() For the "delete" Event. |
||
568 | * @return boolean |
||
569 | */ |
||
570 | public function preDelete() |
||
581 | } |
||
582 |
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.