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 AbstractEvent 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 AbstractEvent, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 22 | abstract class AbstractEvent extends Content implements EventInterface |
||
| 23 | { |
||
| 24 | use CategorizableTrait; |
||
| 25 | use MetatagTrait; |
||
| 26 | use PublishableTrait; |
||
| 27 | use RoutableTrait; |
||
| 28 | use SearchableTrait; |
||
| 29 | use TemplateableTrait; |
||
| 30 | |||
| 31 | /** |
||
| 32 | * @var Translation|string|null |
||
| 33 | */ |
||
| 34 | private $title; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * @var Translation|string|null |
||
| 38 | */ |
||
| 39 | private $subtitle; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * @var Translation|string|null |
||
| 43 | */ |
||
| 44 | private $summary; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * @var Translation|string|null |
||
| 48 | */ |
||
| 49 | private $content; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * @var Translation|string|null |
||
| 53 | */ |
||
| 54 | private $image; |
||
| 55 | |||
| 56 | /** |
||
| 57 | * @var DateTimeInterface|null |
||
| 58 | */ |
||
| 59 | private $startDate; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * @var DateTimeInterface|null |
||
| 63 | */ |
||
| 64 | private $endDate; |
||
| 65 | |||
| 66 | /** |
||
| 67 | * @var Translation|string|null |
||
| 68 | */ |
||
| 69 | private $infoUrl; |
||
| 70 | |||
| 71 | /** |
||
| 72 | * @var string |
||
| 73 | */ |
||
| 74 | private $infoPhone; |
||
| 75 | |||
| 76 | |||
| 77 | /** |
||
| 78 | * @var float|null |
||
| 79 | */ |
||
| 80 | private $ticketPriceMin; |
||
| 81 | |||
| 82 | /** |
||
| 83 | * @var float|null |
||
| 84 | */ |
||
| 85 | private $ticketPriceMax; |
||
| 86 | |||
| 87 | /** |
||
| 88 | * @var Translation|string|null |
||
| 89 | */ |
||
| 90 | private $ticketSummary; |
||
| 91 | |||
| 92 | /** |
||
| 93 | * @var Translation|string|null |
||
| 94 | */ |
||
| 95 | private $ticketUrl; |
||
| 96 | |||
| 97 | /** |
||
| 98 | * @var string |
||
| 99 | */ |
||
| 100 | private $ticketPhone; |
||
| 101 | |||
| 102 | /** |
||
| 103 | * @var array |
||
| 104 | */ |
||
| 105 | protected $keywords; |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Section constructor. |
||
| 109 | * @param array $data The data. |
||
| 110 | */ |
||
| 111 | public function __construct(array $data = null) |
||
| 119 | |||
| 120 | /** |
||
| 121 | * @see MetataTrait::canonicalUrl |
||
| 122 | * @return string |
||
| 123 | */ |
||
| 124 | public function canonicalUrl() |
||
| 128 | |||
| 129 | /** |
||
| 130 | * Some dates cannot be null |
||
| 131 | * @return void |
||
| 132 | */ |
||
| 133 | public function verifyDates() |
||
| 147 | |||
| 148 | /** |
||
| 149 | * @return string The date filtered for admin dual select input and others. |
||
| 150 | */ |
||
| 151 | public function adminDateFilter() |
||
| 164 | |||
| 165 | /** |
||
| 166 | * @param mixed $title The event title (localized). |
||
| 167 | * @return self |
||
| 168 | */ |
||
| 169 | public function setTitle($title) |
||
| 175 | |||
| 176 | /** |
||
| 177 | * @return Translation|string|null |
||
| 178 | */ |
||
| 179 | public function title() |
||
| 183 | |||
| 184 | /** |
||
| 185 | * @param mixed $subtitle The event subtitle (localized). |
||
| 186 | * @return self |
||
| 187 | */ |
||
| 188 | public function setSubtitle($subtitle) |
||
| 194 | |||
| 195 | /** |
||
| 196 | * @return Translation|string|null |
||
| 197 | */ |
||
| 198 | public function subtitle() |
||
| 202 | |||
| 203 | /** |
||
| 204 | * @param mixed $summary The news summary (localized). |
||
| 205 | * @return self |
||
| 206 | */ |
||
| 207 | public function setSummary($summary) |
||
| 213 | |||
| 214 | /** |
||
| 215 | * @return Translation|string|null |
||
| 216 | */ |
||
| 217 | public function summary() |
||
| 221 | |||
| 222 | /** |
||
| 223 | * @param mixed $content The event content (localized). |
||
| 224 | * @return self |
||
| 225 | */ |
||
| 226 | public function setContent($content) |
||
| 232 | |||
| 233 | /** |
||
| 234 | * @return Translation|string|null |
||
| 235 | */ |
||
| 236 | public function content() |
||
| 240 | |||
| 241 | /** |
||
| 242 | * @param mixed $image The section main image (localized). |
||
| 243 | * @return self |
||
| 244 | */ |
||
| 245 | public function setImage($image) |
||
| 251 | |||
| 252 | /** |
||
| 253 | * @return Translation|string|null |
||
| 254 | */ |
||
| 255 | public function image() |
||
| 259 | |||
| 260 | /** |
||
| 261 | * @param string|DateTimeInterface|null $startDate Event starting date. |
||
| 262 | * @throws InvalidArgumentException If the timestamp is invalid. |
||
| 263 | * @return self |
||
| 264 | */ |
||
| 265 | View Code Duplication | public function setStartDate($startDate) |
|
| 284 | |||
| 285 | /** |
||
| 286 | * @return DateTimeInterface|null |
||
| 287 | */ |
||
| 288 | public function startDate() |
||
| 292 | |||
| 293 | /** |
||
| 294 | * @param string|DateTimeInterface|null $endDate Event end date. |
||
| 295 | * @throws InvalidArgumentException If the timestamp is invalid. |
||
| 296 | * @return self |
||
| 297 | */ |
||
| 298 | View Code Duplication | public function setEndDate($endDate) |
|
| 317 | |||
| 318 | /** |
||
| 319 | * @return DateTimeInterface|null |
||
| 320 | */ |
||
| 321 | public function endDate() |
||
| 325 | |||
| 326 | /** |
||
| 327 | * @param mixed $url The information URL (localized). |
||
| 328 | * @return self |
||
| 329 | */ |
||
| 330 | public function setInfoUrl($url) |
||
| 335 | |||
| 336 | /** |
||
| 337 | * @return Translation|null|string |
||
| 338 | */ |
||
| 339 | public function infoUrl() |
||
| 343 | |||
| 344 | /** |
||
| 345 | * @param mixed $phone General information phone number. |
||
| 346 | * @return self |
||
| 347 | */ |
||
| 348 | public function setInfoPhone($phone) |
||
| 353 | |||
| 354 | /** |
||
| 355 | * @return string|null |
||
| 356 | */ |
||
| 357 | public function infoPhone() |
||
| 361 | |||
| 362 | /** |
||
| 363 | * @param float $price The minimum ticket price. |
||
| 364 | * @return self |
||
| 365 | */ |
||
| 366 | public function setTicketPriceMin($price) |
||
| 371 | |||
| 372 | /** |
||
| 373 | * @return float|null |
||
| 374 | */ |
||
| 375 | public function ticketPriceMin() |
||
| 379 | |||
| 380 | /** |
||
| 381 | * @param float $price The maximum ticket price. |
||
| 382 | * @return self |
||
| 383 | */ |
||
| 384 | public function setTicketPriceMax($price) |
||
| 389 | |||
| 390 | /** |
||
| 391 | * @return float|null |
||
| 392 | */ |
||
| 393 | public function ticketPriceMax() |
||
| 397 | |||
| 398 | /** |
||
| 399 | * @param mixed $summary The ticket summary / information (localized). |
||
| 400 | * @return self |
||
| 401 | */ |
||
| 402 | public function setTicketSummary($summary) |
||
| 407 | |||
| 408 | /** |
||
| 409 | * @return Translation|null |
||
| 410 | */ |
||
| 411 | public function ticketSummary() |
||
| 415 | |||
| 416 | /** |
||
| 417 | * @param mixed $url The ticket URL (localized). |
||
| 418 | * @return self |
||
| 419 | */ |
||
| 420 | public function setTicketUrl($url) |
||
| 425 | |||
| 426 | /** |
||
| 427 | * @return Translation|null |
||
| 428 | */ |
||
| 429 | public function ticketUrl() |
||
| 433 | |||
| 434 | /** |
||
| 435 | * @param string|null $phone Tickets phone number. |
||
| 436 | * @return self |
||
| 437 | */ |
||
| 438 | public function setTicketPhone($phone) |
||
| 443 | |||
| 444 | /** |
||
| 445 | * @return string|null |
||
| 446 | */ |
||
| 447 | public function ticketPhone() |
||
| 451 | |||
| 452 | /** |
||
| 453 | * @return Translation|null |
||
| 454 | */ |
||
| 455 | public function defaultMetaTitle() |
||
| 459 | |||
| 460 | /** |
||
| 461 | * @return Translation|string|null |
||
| 462 | */ |
||
| 463 | View Code Duplication | public function defaultMetaDescription() |
|
| 477 | |||
| 478 | /** |
||
| 479 | * @return Translation|string|null |
||
| 480 | */ |
||
| 481 | public function defaultMetaImage() |
||
| 485 | |||
| 486 | /** |
||
| 487 | * Retrieve the object's keywords. |
||
| 488 | * |
||
| 489 | * @return string[] |
||
| 490 | */ |
||
| 491 | public function keywords() |
||
| 495 | |||
| 496 | /** |
||
| 497 | * GenericRoute checks if the route is active. |
||
| 498 | * Default in RoutableTrait. |
||
| 499 | * |
||
| 500 | * @return boolean |
||
| 501 | */ |
||
| 502 | public function isActiveRoute() |
||
| 509 | |||
| 510 | /** |
||
| 511 | * {@inheritdoc} |
||
| 512 | * |
||
| 513 | * @return boolean |
||
| 514 | */ |
||
| 515 | protected function preSave() |
||
| 522 | |||
| 523 | /** |
||
| 524 | * {@inheritdoc} |
||
| 525 | * |
||
| 526 | * @param array $properties Optional properties to update. |
||
| 527 | * @return boolean |
||
| 528 | */ |
||
| 529 | protected function preUpdate(array $properties = null) |
||
| 536 | |||
| 537 | /** |
||
| 538 | * @return boolean Parent postSave(). |
||
| 539 | */ |
||
| 540 | protected function postSave() |
||
| 547 | |||
| 548 | /** |
||
| 549 | * @param array|null $properties Properties. |
||
| 550 | * @return boolean |
||
| 551 | */ |
||
| 552 | protected function postUpdate(array $properties = null) |
||
| 559 | } |
||
| 560 |
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.