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 |
||
51 | abstract class OfferLDProjector implements OrganizerServiceInterface |
||
52 | { |
||
53 | use MultilingualJsonLDProjectorTrait; |
||
54 | use DelegateEventHandlingToSpecificMethodTrait { |
||
55 | DelegateEventHandlingToSpecificMethodTrait::handle as handleUnknownEvents; |
||
56 | } |
||
57 | |||
58 | /** |
||
59 | * @var DocumentRepositoryInterface |
||
60 | */ |
||
61 | protected $repository; |
||
62 | |||
63 | /** |
||
64 | * @var IriGeneratorInterface |
||
65 | */ |
||
66 | protected $iriGenerator; |
||
67 | |||
68 | /** |
||
69 | * @var EntityServiceInterface |
||
70 | */ |
||
71 | protected $organizerService; |
||
72 | |||
73 | /** |
||
74 | * @var JsonDocumentMetaDataEnricherInterface |
||
75 | */ |
||
76 | protected $jsonDocumentMetaDataEnricher; |
||
77 | |||
78 | /** |
||
79 | * @var SerializerInterface |
||
80 | */ |
||
81 | protected $mediaObjectSerializer; |
||
82 | |||
83 | /** |
||
84 | * @var SluggerInterface |
||
85 | */ |
||
86 | protected $slugger; |
||
87 | |||
88 | /** |
||
89 | * @param DocumentRepositoryInterface $repository |
||
90 | * @param IriGeneratorInterface $iriGenerator |
||
91 | * @param EntityServiceInterface $organizerService |
||
92 | * @param SerializerInterface $mediaObjectSerializer |
||
93 | * @param JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher |
||
94 | */ |
||
95 | public function __construct( |
||
110 | |||
111 | /** |
||
112 | * {@inheritdoc} |
||
113 | */ |
||
114 | public function handle(DomainMessage $domainMessage) |
||
143 | |||
144 | /** |
||
145 | * @return string[] |
||
146 | * An associative array of commands and their handler methods. |
||
147 | */ |
||
148 | View Code Duplication | private function getEventHandlers() |
|
168 | |||
169 | /** |
||
170 | * @return string |
||
171 | */ |
||
172 | abstract protected function getLabelAddedClassName(); |
||
173 | |||
174 | /** |
||
175 | * @return string |
||
176 | */ |
||
177 | abstract protected function getLabelRemovedClassName(); |
||
178 | |||
179 | /** |
||
180 | * @return string |
||
181 | */ |
||
182 | abstract protected function getImageAddedClassName(); |
||
183 | |||
184 | /** |
||
185 | * @return string |
||
186 | */ |
||
187 | abstract protected function getImageRemovedClassName(); |
||
188 | |||
189 | /** |
||
190 | * @return string |
||
191 | */ |
||
192 | abstract protected function getImageUpdatedClassName(); |
||
193 | |||
194 | /** |
||
195 | * @return string |
||
196 | */ |
||
197 | abstract protected function getMainImageSelectedClassName(); |
||
198 | |||
199 | /** |
||
200 | * @return string |
||
201 | */ |
||
202 | abstract protected function getTitleTranslatedClassName(); |
||
203 | |||
204 | /** |
||
205 | * @return string |
||
206 | */ |
||
207 | abstract protected function getTitleUpdatedClassName(); |
||
208 | |||
209 | /** |
||
210 | * @return string |
||
211 | */ |
||
212 | abstract protected function getDescriptionTranslatedClassName(); |
||
213 | |||
214 | /** |
||
215 | * @return string |
||
216 | */ |
||
217 | abstract protected function getOrganizerUpdatedClassName(); |
||
218 | |||
219 | /** |
||
220 | * @return string |
||
221 | */ |
||
222 | abstract protected function getOrganizerDeletedClassName(); |
||
223 | |||
224 | /** |
||
225 | * @return string |
||
226 | */ |
||
227 | abstract protected function getBookingInfoUpdatedClassName(); |
||
228 | |||
229 | /** |
||
230 | * @return string |
||
231 | */ |
||
232 | abstract protected function getPriceInfoUpdatedClassName(); |
||
233 | |||
234 | /** |
||
235 | * @return string |
||
236 | */ |
||
237 | abstract protected function getContactPointUpdatedClassName(); |
||
238 | |||
239 | /** |
||
240 | * @return string |
||
241 | */ |
||
242 | abstract protected function getDescriptionUpdatedClassName(); |
||
243 | |||
244 | /** |
||
245 | * @return string |
||
246 | */ |
||
247 | abstract protected function getCalendarUpdatedClassName(); |
||
248 | |||
249 | /** |
||
250 | * @return string |
||
251 | */ |
||
252 | abstract protected function getTypicalAgeRangeUpdatedClassName(); |
||
253 | |||
254 | /** |
||
255 | * @return string |
||
256 | */ |
||
257 | abstract protected function getTypicalAgeRangeDeletedClassName(); |
||
258 | |||
259 | /** |
||
260 | * @return string |
||
261 | */ |
||
262 | abstract protected function getPublishedClassName(); |
||
263 | |||
264 | /** |
||
265 | * @return string |
||
266 | */ |
||
267 | abstract protected function getApprovedClassName(); |
||
268 | |||
269 | /** |
||
270 | * @return string |
||
271 | */ |
||
272 | abstract protected function getRejectedClassName(); |
||
273 | |||
274 | /** |
||
275 | * @return string |
||
276 | */ |
||
277 | abstract protected function getFlaggedAsDuplicateClassName(); |
||
278 | |||
279 | /** |
||
280 | * @return string |
||
281 | */ |
||
282 | abstract protected function getFlaggedAsInappropriateClassName(); |
||
283 | |||
284 | /** |
||
285 | * @return string |
||
286 | */ |
||
287 | abstract protected function getImagesImportedFromUdb2ClassName(); |
||
288 | |||
289 | /** |
||
290 | * @return string |
||
291 | */ |
||
292 | abstract protected function getImagesUpdatedFromUdb2ClassName(); |
||
293 | |||
294 | /** |
||
295 | * @param AbstractLabelAdded $labelAdded |
||
296 | * @return JsonDocument |
||
297 | */ |
||
298 | protected function applyLabelAdded(AbstractLabelAdded $labelAdded) |
||
315 | |||
316 | /** |
||
317 | * @param AbstractLabelRemoved $labelRemoved |
||
318 | * @return JsonDocument |
||
319 | */ |
||
320 | View Code Duplication | protected function applyLabelRemoved(AbstractLabelRemoved $labelRemoved) |
|
352 | |||
353 | /** |
||
354 | * Apply the imageAdded event to the item repository. |
||
355 | * |
||
356 | * @param AbstractImageAdded $imageAdded |
||
357 | * @return JsonDocument |
||
358 | */ |
||
359 | protected function applyImageAdded(AbstractImageAdded $imageAdded) |
||
376 | |||
377 | /** |
||
378 | * Apply the ImageUpdated event to the item repository. |
||
379 | * |
||
380 | * @param AbstractImageUpdated $imageUpdated |
||
381 | * @return JsonDocument |
||
382 | * @throws \Exception |
||
383 | */ |
||
384 | protected function applyImageUpdated(AbstractImageUpdated $imageUpdated) |
||
418 | |||
419 | /** |
||
420 | * @param AbstractImageRemoved $imageRemoved |
||
421 | * @return JsonDocument |
||
422 | */ |
||
423 | protected function applyImageRemoved(AbstractImageRemoved $imageRemoved) |
||
474 | |||
475 | /** |
||
476 | * @param AbstractMainImageSelected $mainImageSelected |
||
477 | * @return JsonDocument |
||
478 | */ |
||
479 | protected function applyMainImageSelected(AbstractMainImageSelected $mainImageSelected) |
||
500 | |||
501 | /** |
||
502 | * @param Object $mediaObject |
||
503 | * @param UUID $mediaObjectId |
||
504 | * |
||
505 | * @return bool |
||
506 | */ |
||
507 | protected function mediaObjectMatchesId($mediaObject, UUID $mediaObjectId) |
||
511 | |||
512 | /** |
||
513 | * @param AbstractTitleTranslated $titleTranslated |
||
514 | * @return JsonDocument |
||
515 | */ |
||
516 | protected function applyTitleTranslated(AbstractTitleTranslated $titleTranslated) |
||
526 | |||
527 | /** |
||
528 | * @param AbstractTitleUpdated $titleUpdated |
||
529 | * @return JsonDocument |
||
530 | */ |
||
531 | protected function applyTitleUpdated(AbstractTitleUpdated $titleUpdated) |
||
541 | |||
542 | /** |
||
543 | * @param AbstractDescriptionTranslated $descriptionTranslated |
||
544 | * @return JsonDocument |
||
545 | */ |
||
546 | protected function applyDescriptionTranslated( |
||
561 | |||
562 | /** |
||
563 | * @param AbstractCalendarUpdated $calendarUpdated |
||
564 | * |
||
565 | * @return JsonDocument |
||
566 | */ |
||
567 | protected function applyCalendarUpdated(AbstractCalendarUpdated $calendarUpdated) |
||
579 | |||
580 | /** |
||
581 | * Apply the organizer updated event to the offer repository. |
||
582 | * @param AbstractOrganizerUpdated $organizerUpdated |
||
583 | * @return JsonDocument |
||
584 | */ |
||
585 | protected function applyOrganizerUpdated(AbstractOrganizerUpdated $organizerUpdated) |
||
597 | |||
598 | /** |
||
599 | * Apply the organizer delete event to the offer repository. |
||
600 | * @param AbstractOrganizerDeleted $organizerDeleted |
||
601 | * @return JsonDocument |
||
602 | */ |
||
603 | protected function applyOrganizerDeleted(AbstractOrganizerDeleted $organizerDeleted) |
||
613 | |||
614 | /** |
||
615 | * Apply the booking info updated event to the offer repository. |
||
616 | * @param AbstractBookingInfoUpdated $bookingInfoUpdated |
||
617 | * @return JsonDocument |
||
618 | */ |
||
619 | protected function applyBookingInfoUpdated(AbstractBookingInfoUpdated $bookingInfoUpdated) |
||
628 | |||
629 | /** |
||
630 | * @param AbstractPriceInfoUpdated $priceInfoUpdated |
||
631 | * @return JsonDocument |
||
632 | */ |
||
633 | protected function applyPriceInfoUpdated(AbstractPriceInfoUpdated $priceInfoUpdated) |
||
660 | |||
661 | /** |
||
662 | * Apply the contact point updated event to the offer repository. |
||
663 | * @param AbstractContactPointUpdated $contactPointUpdated |
||
664 | * @return JsonDocument |
||
665 | */ |
||
666 | protected function applyContactPointUpdated(AbstractContactPointUpdated $contactPointUpdated) |
||
675 | |||
676 | /** |
||
677 | * Apply the description updated event to the offer repository. |
||
678 | * @param AbstractDescriptionUpdated $descriptionUpdated |
||
679 | * @return JsonDocument |
||
680 | */ |
||
681 | protected function applyDescriptionUpdated( |
||
694 | |||
695 | /** |
||
696 | * Apply the typical age range updated event to the offer repository. |
||
697 | * @param AbstractTypicalAgeRangeUpdated $typicalAgeRangeUpdated |
||
698 | * @return JsonDocument |
||
699 | */ |
||
700 | protected function applyTypicalAgeRangeUpdated( |
||
710 | |||
711 | /** |
||
712 | * Apply the typical age range deleted event to the offer repository. |
||
713 | * @param AbstractTypicalAgeRangeDeleted $typicalAgeRangeDeleted |
||
714 | * @return JsonDocument |
||
715 | */ |
||
716 | protected function applyTypicalAgeRangeDeleted( |
||
727 | |||
728 | /** |
||
729 | * @param AbstractPublished $published |
||
730 | * @return JsonDocument |
||
731 | */ |
||
732 | protected function applyPublished(AbstractPublished $published) |
||
745 | |||
746 | /** |
||
747 | * @param AbstractApproved $approved |
||
748 | * @return JsonDocument |
||
749 | */ |
||
750 | protected function applyApproved(AbstractApproved $approved) |
||
757 | |||
758 | /** |
||
759 | * @param AbstractRejected $rejected |
||
760 | * @return JsonDocument |
||
761 | */ |
||
762 | View Code Duplication | protected function applyRejected(AbstractRejected $rejected) |
|
769 | |||
770 | /** |
||
771 | * @param AbstractFlaggedAsDuplicate $flaggedAsDuplicate |
||
772 | * @return JsonDocument |
||
773 | */ |
||
774 | View Code Duplication | protected function applyFlaggedAsDuplicate( |
|
782 | |||
783 | /** |
||
784 | * @param AbstractFlaggedAsInappropriate $flaggedAsInappropriate |
||
785 | * @return JsonDocument |
||
786 | */ |
||
787 | View Code Duplication | protected function applyFlaggedAsInappropriate( |
|
795 | |||
796 | /** |
||
797 | * @param AbstractImagesImportedFromUDB2 $imagesImportedFromUDB2 |
||
798 | * @return JsonDocument |
||
799 | */ |
||
800 | View Code Duplication | protected function applyImagesImportedFromUdb2(AbstractImagesImportedFromUDB2 $imagesImportedFromUDB2) |
|
807 | |||
808 | /** |
||
809 | * @param AbstractImagesUpdatedFromUDB2 $imagesUpdatedFromUDB2 |
||
810 | * @return JsonDocument |
||
811 | */ |
||
812 | View Code Duplication | protected function applyImagesUpdatedFromUdb2(AbstractImagesUpdatedFromUDB2 $imagesUpdatedFromUDB2) |
|
819 | |||
820 | /** |
||
821 | * This indirect apply method can be called internally to deal with images coming from UDB2. |
||
822 | * Imports from UDB2 only contain the native Dutch content. |
||
823 | * @see https://github.com/cultuurnet/udb3-udb2-bridge/blob/db0a7ab2444f55bb3faae3d59b82b39aaeba253b/test/Media/ImageCollectionFactoryTest.php#L79-L103 |
||
824 | * Because of this we have to make sure translated images are left in place. |
||
825 | * |
||
826 | * @param \stdClass $offerLd |
||
827 | * @param AbstractImagesEvent $imagesEvent |
||
828 | */ |
||
829 | private function applyUdb2ImagesEvent(\stdClass $offerLd, AbstractImagesEvent $imagesEvent) |
||
857 | |||
858 | /** |
||
859 | * @param string $id |
||
860 | * @return JsonDocument |
||
861 | */ |
||
862 | View Code Duplication | protected function newDocument($id) |
|
871 | |||
872 | /** |
||
873 | * @param AbstractEvent $event |
||
874 | * @return JsonDocument |
||
875 | */ |
||
876 | protected function loadDocumentFromRepository(AbstractEvent $event) |
||
880 | |||
881 | /** |
||
882 | * @param string $itemId |
||
883 | * @return JsonDocument |
||
884 | */ |
||
885 | protected function loadDocumentFromRepositoryByItemId($itemId) |
||
895 | |||
896 | /** |
||
897 | * @inheritdoc |
||
898 | */ |
||
899 | public function organizerJSONLD($organizerId) |
||
914 | } |
||
915 |
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.