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 OfferLDProjector 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 OfferLDProjector, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 48 | abstract class OfferLDProjector implements OrganizerServiceInterface |
||
| 49 | { |
||
| 50 | use DelegateEventHandlingToSpecificMethodTrait { |
||
| 51 | DelegateEventHandlingToSpecificMethodTrait::handle as handleUnknownEvents; |
||
| 52 | } |
||
| 53 | |||
| 54 | /** |
||
| 55 | * @var DocumentRepositoryInterface |
||
| 56 | */ |
||
| 57 | protected $repository; |
||
| 58 | |||
| 59 | /** |
||
| 60 | * @var IriGeneratorInterface |
||
| 61 | */ |
||
| 62 | protected $iriGenerator; |
||
| 63 | |||
| 64 | /** |
||
| 65 | * @var EntityServiceInterface |
||
| 66 | */ |
||
| 67 | protected $organizerService; |
||
| 68 | |||
| 69 | /** |
||
| 70 | * @var SluggerInterface |
||
| 71 | */ |
||
| 72 | protected $slugger; |
||
| 73 | |||
| 74 | /** |
||
| 75 | * @var CdbXMLImporter |
||
| 76 | */ |
||
| 77 | protected $cdbXMLImporter; |
||
| 78 | |||
| 79 | /** |
||
| 80 | * @var SerializerInterface |
||
| 81 | */ |
||
| 82 | protected $mediaObjectSerializer; |
||
| 83 | |||
| 84 | /** |
||
| 85 | * @param DocumentRepositoryInterface $repository |
||
| 86 | * @param IriGeneratorInterface $iriGenerator |
||
| 87 | * @param EntityServiceInterface $organizerService |
||
| 88 | * @param SerializerInterface $mediaObjectSerializer |
||
| 89 | * @param EventCdbIdExtractorInterface $eventCdbIdExtractor |
||
| 90 | * @param CalendarFactoryInterface $calendarFactory |
||
| 91 | */ |
||
| 92 | public function __construct( |
||
| 117 | |||
| 118 | /** |
||
| 119 | * {@inheritdoc} |
||
| 120 | */ |
||
| 121 | View Code Duplication | public function handle(DomainMessage $domainMessage) |
|
| 135 | |||
| 136 | /** |
||
| 137 | * @return string[] |
||
| 138 | * An associative array of commands and their handler methods. |
||
| 139 | */ |
||
| 140 | View Code Duplication | private function getEventHandlers() |
|
| 160 | |||
| 161 | /** |
||
| 162 | * @return string |
||
| 163 | */ |
||
| 164 | abstract protected function getLabelAddedClassName(); |
||
| 165 | |||
| 166 | /** |
||
| 167 | * @return string |
||
| 168 | */ |
||
| 169 | abstract protected function getLabelRemovedClassName(); |
||
| 170 | |||
| 171 | /** |
||
| 172 | * @return string |
||
| 173 | */ |
||
| 174 | abstract protected function getImageAddedClassName(); |
||
| 175 | |||
| 176 | /** |
||
| 177 | * @return string |
||
| 178 | */ |
||
| 179 | abstract protected function getImageRemovedClassName(); |
||
| 180 | |||
| 181 | /** |
||
| 182 | * @return string |
||
| 183 | */ |
||
| 184 | abstract protected function getImageUpdatedClassName(); |
||
| 185 | |||
| 186 | /** |
||
| 187 | * @return string |
||
| 188 | */ |
||
| 189 | abstract protected function getMainImageSelectedClassName(); |
||
| 190 | |||
| 191 | /** |
||
| 192 | * @return string |
||
| 193 | */ |
||
| 194 | abstract protected function getTitleTranslatedClassName(); |
||
| 195 | |||
| 196 | /** |
||
| 197 | * @return string |
||
| 198 | */ |
||
| 199 | abstract protected function getDescriptionTranslatedClassName(); |
||
| 200 | |||
| 201 | /** |
||
| 202 | * @return string |
||
| 203 | */ |
||
| 204 | abstract protected function getOrganizerUpdatedClassName(); |
||
| 205 | |||
| 206 | /** |
||
| 207 | * @return string |
||
| 208 | */ |
||
| 209 | abstract protected function getOrganizerDeletedClassName(); |
||
| 210 | |||
| 211 | /** |
||
| 212 | * @return string |
||
| 213 | */ |
||
| 214 | abstract protected function getBookingInfoUpdatedClassName(); |
||
| 215 | |||
| 216 | /** |
||
| 217 | * @return string |
||
| 218 | */ |
||
| 219 | abstract protected function getPriceInfoUpdatedClassName(); |
||
| 220 | |||
| 221 | /** |
||
| 222 | * @return string |
||
| 223 | */ |
||
| 224 | abstract protected function getContactPointUpdatedClassName(); |
||
| 225 | |||
| 226 | /** |
||
| 227 | * @return string |
||
| 228 | */ |
||
| 229 | abstract protected function getDescriptionUpdatedClassName(); |
||
| 230 | |||
| 231 | /** |
||
| 232 | * @return string |
||
| 233 | */ |
||
| 234 | abstract protected function getTypicalAgeRangeUpdatedClassName(); |
||
| 235 | |||
| 236 | /** |
||
| 237 | * @return string |
||
| 238 | */ |
||
| 239 | abstract protected function getTypicalAgeRangeDeletedClassName(); |
||
| 240 | |||
| 241 | /** |
||
| 242 | * @return string |
||
| 243 | */ |
||
| 244 | abstract protected function getPublishedClassName(); |
||
| 245 | |||
| 246 | /** |
||
| 247 | * @return string |
||
| 248 | */ |
||
| 249 | abstract protected function getApprovedClassName(); |
||
| 250 | |||
| 251 | /** |
||
| 252 | * @return string |
||
| 253 | */ |
||
| 254 | abstract protected function getRejectedClassName(); |
||
| 255 | |||
| 256 | /** |
||
| 257 | * @return string |
||
| 258 | */ |
||
| 259 | abstract protected function getFlaggedAsDuplicateClassName(); |
||
| 260 | |||
| 261 | /** |
||
| 262 | * @return string |
||
| 263 | */ |
||
| 264 | abstract protected function getFlaggedAsInappropriateClassName(); |
||
| 265 | |||
| 266 | /** |
||
| 267 | * @param AbstractLabelAdded $labelAdded |
||
| 268 | */ |
||
| 269 | View Code Duplication | protected function applyLabelAdded(AbstractLabelAdded $labelAdded) |
|
| 286 | |||
| 287 | /** |
||
| 288 | * @param AbstractLabelRemoved $labelRemoved |
||
| 289 | */ |
||
| 290 | View Code Duplication | protected function applyLabelRemoved(AbstractLabelRemoved $labelRemoved) |
|
| 322 | |||
| 323 | /** |
||
| 324 | * Apply the imageAdded event to the item repository. |
||
| 325 | * |
||
| 326 | * @param AbstractImageAdded $imageAdded |
||
| 327 | */ |
||
| 328 | protected function applyImageAdded(AbstractImageAdded $imageAdded) |
||
| 345 | |||
| 346 | /** |
||
| 347 | * Apply the ImageUpdated event to the item repository. |
||
| 348 | * |
||
| 349 | * @param AbstractImageUpdated $imageUpdated |
||
| 350 | * @throws \Exception |
||
| 351 | */ |
||
| 352 | protected function applyImageUpdated(AbstractImageUpdated $imageUpdated) |
||
| 386 | |||
| 387 | /** |
||
| 388 | * @param AbstractImageRemoved $imageRemoved |
||
| 389 | */ |
||
| 390 | protected function applyImageRemoved(AbstractImageRemoved $imageRemoved) |
||
| 441 | |||
| 442 | /** |
||
| 443 | * @param AbstractMainImageSelected $mainImageSelected |
||
| 444 | */ |
||
| 445 | protected function applyMainImageSelected(AbstractMainImageSelected $mainImageSelected) |
||
| 466 | |||
| 467 | /** |
||
| 468 | * @param Object $mediaObject |
||
| 469 | * @param UUID $mediaObjectId |
||
| 470 | * |
||
| 471 | * @return bool |
||
| 472 | */ |
||
| 473 | protected function mediaObjectMatchesId($mediaObject, UUID $mediaObjectId) |
||
| 477 | |||
| 478 | /** |
||
| 479 | * @param AbstractTitleTranslated $titleTranslated |
||
| 480 | */ |
||
| 481 | protected function applyTitleTranslated(AbstractTitleTranslated $titleTranslated) |
||
| 491 | |||
| 492 | /** |
||
| 493 | * @param AbstractDescriptionTranslated $descriptionTranslated |
||
| 494 | */ |
||
| 495 | protected function applyDescriptionTranslated( |
||
| 510 | |||
| 511 | /** |
||
| 512 | * Apply the organizer updated event to the offer repository. |
||
| 513 | * @param AbstractOrganizerUpdated $organizerUpdated |
||
| 514 | */ |
||
| 515 | protected function applyOrganizerUpdated(AbstractOrganizerUpdated $organizerUpdated) |
||
| 527 | |||
| 528 | /** |
||
| 529 | * Apply the organizer delete event to the offer repository. |
||
| 530 | * @param AbstractOrganizerDeleted $organizerDeleted |
||
| 531 | */ |
||
| 532 | protected function applyOrganizerDeleted(AbstractOrganizerDeleted $organizerDeleted) |
||
| 542 | |||
| 543 | /** |
||
| 544 | * Apply the booking info updated event to the offer repository. |
||
| 545 | * @param AbstractBookingInfoUpdated $bookingInfoUpdated |
||
| 546 | */ |
||
| 547 | View Code Duplication | protected function applyBookingInfoUpdated(AbstractBookingInfoUpdated $bookingInfoUpdated) |
|
| 556 | |||
| 557 | /** |
||
| 558 | * @param AbstractPriceInfoUpdated $priceInfoUpdated |
||
| 559 | */ |
||
| 560 | protected function applyPriceInfoUpdated(AbstractPriceInfoUpdated $priceInfoUpdated) |
||
| 587 | |||
| 588 | /** |
||
| 589 | * Apply the contact point updated event to the offer repository. |
||
| 590 | * @param AbstractContactPointUpdated $contactPointUpdated |
||
| 591 | */ |
||
| 592 | View Code Duplication | protected function applyContactPointUpdated(AbstractContactPointUpdated $contactPointUpdated) |
|
| 601 | |||
| 602 | /** |
||
| 603 | * Apply the description updated event to the offer repository. |
||
| 604 | * @param AbstractDescriptionUpdated $descriptionUpdated |
||
| 605 | */ |
||
| 606 | protected function applyDescriptionUpdated( |
||
| 619 | |||
| 620 | /** |
||
| 621 | * Apply the typical age range updated event to the offer repository. |
||
| 622 | * @param AbstractTypicalAgeRangeUpdated $typicalAgeRangeUpdated |
||
| 623 | */ |
||
| 624 | protected function applyTypicalAgeRangeUpdated( |
||
| 634 | |||
| 635 | /** |
||
| 636 | * Apply the typical age range deleted event to the offer repository. |
||
| 637 | * @param AbstractTypicalAgeRangeDeleted $typicalAgeRangeDeleted |
||
| 638 | */ |
||
| 639 | protected function applyTypicalAgeRangeDeleted( |
||
| 650 | |||
| 651 | /** |
||
| 652 | * @param AbstractPublished $published |
||
| 653 | */ |
||
| 654 | protected function applyPublished(AbstractPublished $published) |
||
| 663 | |||
| 664 | /** |
||
| 665 | * @param AbstractApproved $approved |
||
| 666 | */ |
||
| 667 | protected function applyApproved(AbstractApproved $approved) |
||
| 673 | |||
| 674 | /** |
||
| 675 | * @param AbstractRejected $rejected |
||
| 676 | */ |
||
| 677 | protected function applyRejected(AbstractRejected $rejected) |
||
| 681 | |||
| 682 | /** |
||
| 683 | * @param AbstractFlaggedAsDuplicate $flaggedAsDuplicate |
||
| 684 | */ |
||
| 685 | protected function applyFlaggedAsDuplicate( |
||
| 690 | |||
| 691 | /** |
||
| 692 | * @param AbstractFlaggedAsInappropriate $flaggedAsInappropriate |
||
| 693 | */ |
||
| 694 | protected function applyFlaggedAsInappropriate( |
||
| 699 | |||
| 700 | /** |
||
| 701 | * @return callable |
||
| 702 | */ |
||
| 703 | private function reject() |
||
| 709 | |||
| 710 | /** |
||
| 711 | * @param AbstractEvent $event |
||
| 712 | * @param callable $transformation |
||
| 713 | * a transformation that you want applied to the offer-ld document |
||
| 714 | * the first parameter passed to the callback will be the document body |
||
| 715 | */ |
||
| 716 | private function applyEventTransformation(AbstractEvent $event, callable $transformation) |
||
| 726 | |||
| 727 | /** |
||
| 728 | * @param string $id |
||
| 729 | * @return JsonDocument |
||
| 730 | */ |
||
| 731 | View Code Duplication | protected function newDocument($id) |
|
| 741 | |||
| 742 | /** |
||
| 743 | * @param AbstractEvent $event |
||
| 744 | * @return JsonDocument |
||
| 745 | */ |
||
| 746 | protected function loadDocumentFromRepository(AbstractEvent $event) |
||
| 750 | |||
| 751 | /** |
||
| 752 | * @param string $itemId |
||
| 753 | * @return JsonDocument |
||
| 754 | */ |
||
| 755 | protected function loadDocumentFromRepositoryByItemId($itemId) |
||
| 765 | |||
| 766 | /** |
||
| 767 | * @inheritdoc |
||
| 768 | */ |
||
| 769 | public function organizerJSONLD($organizerId) |
||
| 784 | } |
||
| 785 |
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.