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 ArticleTrait 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 ArticleTrait, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
10 | trait ArticleTrait |
||
11 | { |
||
12 | /** |
||
13 | * The article number. |
||
14 | * |
||
15 | * @var string |
||
16 | * |
||
17 | * @JMS\Type("string") |
||
18 | * @JMS\SerializedName("ARTICLE_NUMBER") |
||
19 | */ |
||
20 | protected $articleNumber; |
||
21 | |||
22 | /** |
||
23 | * The title of the article. |
||
24 | * |
||
25 | * @var string |
||
26 | * |
||
27 | * @JMS\Type("string") |
||
28 | * @JMS\SerializedName("TITLE") |
||
29 | */ |
||
30 | protected $title; |
||
31 | |||
32 | /** |
||
33 | * The description of the article. |
||
34 | * |
||
35 | * @var string |
||
36 | * |
||
37 | * @JMS\Type("string") |
||
38 | * @JMS\SerializedName("DESCRIPTION") |
||
39 | */ |
||
40 | protected $description; |
||
41 | |||
42 | /** |
||
43 | * Article tags. |
||
44 | * |
||
45 | * @var string |
||
46 | * |
||
47 | * @JMS\Type("string") |
||
48 | * @JMS\SerializedName("TAGS") |
||
49 | */ |
||
50 | protected $tags; |
||
51 | |||
52 | /** |
||
53 | * The unit price. |
||
54 | * |
||
55 | * @var float |
||
56 | * |
||
57 | * @JMS\Type("float") |
||
58 | * @JMS\SerializedName("UNIT_PRICE") |
||
59 | */ |
||
60 | protected $unitPrice; |
||
61 | |||
62 | /** |
||
63 | * The setup fee. |
||
64 | * |
||
65 | * @var float |
||
66 | * |
||
67 | * @JMS\Type("float") |
||
68 | * @JMS\SerializedName("SETUP_FEE") |
||
69 | */ |
||
70 | protected $setupFee; |
||
71 | |||
72 | /** |
||
73 | * Allow multiple. |
||
74 | * |
||
75 | * @var integer |
||
76 | * |
||
77 | * @JMS\Type("integer") |
||
78 | * @JMS\SerializedName("ALLOW_MULTIPLE") |
||
79 | */ |
||
80 | protected $allowMultiple; |
||
81 | |||
82 | /** |
||
83 | * Flag if article is addon. |
||
84 | * |
||
85 | * @var integer |
||
86 | * |
||
87 | * @JMS\Type("integer") |
||
88 | * @JMS\SerializedName("IS_ADDON") |
||
89 | */ |
||
90 | protected $isAddon; |
||
91 | |||
92 | /** |
||
93 | * Article translations. |
||
94 | * |
||
95 | * @var Translation |
||
96 | * |
||
97 | * @JMS\Type("Speicher210\Fastbill\Api\Model\Translation") |
||
98 | * @JMS\SerializedName("TRANSLATIONS") |
||
99 | */ |
||
100 | protected $translation; |
||
101 | |||
102 | /** |
||
103 | * The currency code. |
||
104 | * |
||
105 | * @var string |
||
106 | * |
||
107 | * @JMS\Type("string") |
||
108 | * @JMS\SerializedName("CURRENCY_CODE") |
||
109 | */ |
||
110 | protected $currencyCode; |
||
111 | |||
112 | /** |
||
113 | * VAT percent. |
||
114 | * |
||
115 | * @var float |
||
116 | * |
||
117 | * @JMS\Type("float") |
||
118 | * @JMS\SerializedName("VAT_PERCENT") |
||
119 | */ |
||
120 | protected $vatPercent; |
||
121 | |||
122 | /** |
||
123 | * The subscription interval (ex: "1 month"). |
||
124 | * |
||
125 | * @var string |
||
126 | * |
||
127 | * @JMS\Type("string") |
||
128 | * @JMS\SerializedName("SUBSCRIPTION_INTERVAL") |
||
129 | */ |
||
130 | protected $subscriptionInterval; |
||
131 | |||
132 | /** |
||
133 | * Number of billing events for the subscription. |
||
134 | * |
||
135 | * @var integer |
||
136 | * |
||
137 | * @JMS\Type("integer") |
||
138 | * @JMS\SerializedName("SUBSCRIPTION_NUMBER_EVENTS") |
||
139 | */ |
||
140 | protected $subscriptionNumberEvents; |
||
141 | |||
142 | /** |
||
143 | * Subscription trial period (ex: "2 day"). |
||
144 | * |
||
145 | * @var string |
||
146 | * |
||
147 | * @JMS\Type("string") |
||
148 | * @JMS\SerializedName("SUBSCRIPTION_TRIAL") |
||
149 | */ |
||
150 | protected $subscriptionTrial; |
||
151 | |||
152 | /** |
||
153 | * Subscription first contract period (ex: "1 month"). |
||
154 | * |
||
155 | * @var string |
||
156 | * |
||
157 | * @JMS\Type("string") |
||
158 | * @JMS\SerializedName("SUBSCRIPTION_DURATION") |
||
159 | */ |
||
160 | protected $subscriptionDuration; |
||
161 | |||
162 | /** |
||
163 | * Subscription following contract period (ex: "1 month"). |
||
164 | * |
||
165 | * @var string |
||
166 | * |
||
167 | * @JMS\Type("string") |
||
168 | * @JMS\SerializedName("SUBSCRIPTION_DURATION_FOLLOW") |
||
169 | */ |
||
170 | protected $subscriptionDurationFollow; |
||
171 | |||
172 | /** |
||
173 | * Subscription cancellation period (ex: "1 day"). |
||
174 | * |
||
175 | * @var string |
||
176 | * |
||
177 | * @JMS\Type("string") |
||
178 | * @JMS\SerializedName("SUBSCRIPTION_CANCELLATION") |
||
179 | */ |
||
180 | protected $subscriptionCancellationPeriod; |
||
181 | |||
182 | /** |
||
183 | * Success URL. |
||
184 | * |
||
185 | * @var string |
||
186 | * |
||
187 | * @JMS\Type("string") |
||
188 | * @JMS\SerializedName("RETURN_URL_SUCCESS") |
||
189 | */ |
||
190 | protected $returnUrlSuccess; |
||
191 | |||
192 | /** |
||
193 | * Cancel URL. |
||
194 | * |
||
195 | * @var string |
||
196 | * |
||
197 | * @JMS\Type("string") |
||
198 | * @JMS\SerializedName("RETURN_URL_CANCEL") |
||
199 | */ |
||
200 | protected $returnUrlCancel; |
||
201 | |||
202 | /** |
||
203 | * Checkout URL. |
||
204 | * |
||
205 | * @var string |
||
206 | * |
||
207 | * @JMS\Type("string") |
||
208 | * @JMS\SerializedName("CHECKOUT_URL") |
||
209 | */ |
||
210 | protected $checkoutUrl; |
||
211 | |||
212 | /** |
||
213 | * Product features. |
||
214 | * |
||
215 | * @var array |
||
216 | * |
||
217 | * @JMS\Type("array<Speicher210\Fastbill\Api\Model\Feature>") |
||
218 | * @JMS\SerializedName("FEATURES") |
||
219 | */ |
||
220 | protected $features = array(); |
||
221 | |||
222 | /** |
||
223 | * Get the article number. |
||
224 | * |
||
225 | * @return integer |
||
226 | */ |
||
227 | 6 | public function getArticleNumber() |
|
228 | { |
||
229 | 6 | return $this->articleNumber; |
|
230 | } |
||
231 | |||
232 | /** |
||
233 | * Set the article number. |
||
234 | * |
||
235 | * @param string $articleNumber The article number. |
||
236 | * @return $this |
||
237 | */ |
||
238 | 9 | public function setArticleNumber($articleNumber) |
|
244 | |||
245 | /** |
||
246 | * Get the title. |
||
247 | * |
||
248 | * @return string |
||
249 | */ |
||
250 | public function getTitle() |
||
254 | |||
255 | /** |
||
256 | * Set the title. |
||
257 | * |
||
258 | * @param string $title The title. |
||
259 | * @return $this |
||
260 | */ |
||
261 | 6 | public function setTitle($title) |
|
267 | |||
268 | /** |
||
269 | * Get the description. |
||
270 | * |
||
271 | * @return string |
||
272 | */ |
||
273 | public function getDescription() |
||
277 | |||
278 | /** |
||
279 | * Set the description. |
||
280 | * |
||
281 | * @param string $description The description. |
||
282 | * @return $this |
||
283 | */ |
||
284 | 6 | public function setDescription($description) |
|
290 | |||
291 | /** |
||
292 | * Get the tags. |
||
293 | * |
||
294 | * @return array |
||
295 | */ |
||
296 | public function getTags() |
||
302 | |||
303 | /** |
||
304 | * Set the tags. |
||
305 | * # |
||
306 | * @param array $tags The tags to set. |
||
307 | * @return $this |
||
308 | */ |
||
309 | 6 | public function setTags(array $tags) |
|
315 | |||
316 | /** |
||
317 | * Get the unit price. |
||
318 | * |
||
319 | * @return float |
||
320 | */ |
||
321 | public function getUnitPrice() |
||
325 | |||
326 | /** |
||
327 | * Set the unit price. |
||
328 | * |
||
329 | * @param float $unitPrice The price. |
||
330 | * @return $this |
||
331 | */ |
||
332 | 6 | public function setUnitPrice($unitPrice) |
|
338 | |||
339 | /** |
||
340 | * Get the setup fee. |
||
341 | * |
||
342 | * @return float |
||
343 | */ |
||
344 | public function getSetupFee() |
||
348 | |||
349 | /** |
||
350 | * Set the setup fee. |
||
351 | * |
||
352 | * @param float $setupFee The setup fee. |
||
353 | * @return $this |
||
354 | */ |
||
355 | 6 | public function setSetupFee($setupFee) |
|
361 | |||
362 | /** |
||
363 | * Check if it allows multiple instances. |
||
364 | * |
||
365 | * @return boolean |
||
366 | */ |
||
367 | public function allowsMultiple() |
||
371 | |||
372 | /** |
||
373 | * Set if it allows multiple instances. |
||
374 | * |
||
375 | * @param boolean $allowMultiple The flag. |
||
376 | * @return $this |
||
377 | */ |
||
378 | 6 | public function setAllowMultiple($allowMultiple) |
|
384 | |||
385 | /** |
||
386 | * Check if the article is an addon. |
||
387 | * |
||
388 | * @return boolean |
||
389 | */ |
||
390 | public function isAddon() |
||
394 | |||
395 | /** |
||
396 | * Set that the article is an addon. |
||
397 | * |
||
398 | * @param boolean $isAddon Flag if the article is an addon. |
||
399 | * @return $this |
||
400 | */ |
||
401 | 6 | public function setIsAddon($isAddon) |
|
407 | |||
408 | /** |
||
409 | * Get the translation. |
||
410 | * |
||
411 | * @return Translation |
||
412 | */ |
||
413 | public function getTranslation() |
||
417 | |||
418 | /** |
||
419 | * Set the translation. |
||
420 | * |
||
421 | * @param Translation $translation The translation to set. |
||
422 | * @return $this |
||
423 | */ |
||
424 | 6 | public function setTranslation(Translation $translation) |
|
430 | |||
431 | /** |
||
432 | * Get the currency code. |
||
433 | * |
||
434 | * @return string |
||
435 | */ |
||
436 | public function getCurrencyCode() |
||
440 | |||
441 | /** |
||
442 | * Set the currency code. |
||
443 | * |
||
444 | * @param string $currencyCode The currency code. |
||
445 | * @return $this |
||
446 | */ |
||
447 | 6 | public function setCurrencyCode($currencyCode) |
|
453 | |||
454 | /** |
||
455 | * Get the VAT percentage. |
||
456 | * |
||
457 | * @return float |
||
458 | */ |
||
459 | public function getVatPercent() |
||
463 | |||
464 | /** |
||
465 | * Set the VAT percentage. |
||
466 | * |
||
467 | * @param float $vatPercent The VAT percentage. |
||
468 | * @return $this |
||
469 | */ |
||
470 | 6 | View Code Duplication | public function setVatPercent($vatPercent) |
479 | |||
480 | /** |
||
481 | * Get the subscription interval. |
||
482 | * |
||
483 | * @return string |
||
484 | */ |
||
485 | public function getSubscriptionInterval() |
||
489 | |||
490 | /** |
||
491 | * Set the subscription interval. |
||
492 | * |
||
493 | * @param string $subscriptionInterval The interval. |
||
494 | * @return $this |
||
495 | */ |
||
496 | 6 | public function setSubscriptionInterval($subscriptionInterval) |
|
502 | |||
503 | /** |
||
504 | * Get the subscription number of events. |
||
505 | * |
||
506 | * @return integer |
||
507 | */ |
||
508 | public function getSubscriptionNumberEvents() |
||
512 | |||
513 | /** |
||
514 | * Set the subscription number of events. |
||
515 | * |
||
516 | * @param integer $subscriptionNumberEvents The number of events. |
||
517 | * @return $this |
||
518 | */ |
||
519 | 6 | public function setSubscriptionNumberEvents($subscriptionNumberEvents) |
|
525 | |||
526 | /** |
||
527 | * Get the subscription trial. |
||
528 | * |
||
529 | * @return string |
||
530 | */ |
||
531 | public function getSubscriptionTrial() |
||
535 | |||
536 | /** |
||
537 | * Set the subscription trial. |
||
538 | * |
||
539 | * @param string $subscriptionTrial The subscription trial. |
||
540 | * @return $this |
||
541 | */ |
||
542 | 6 | public function setSubscriptionTrial($subscriptionTrial) |
|
548 | |||
549 | /** |
||
550 | * Get the subscription duration. |
||
551 | * |
||
552 | * @return string |
||
553 | */ |
||
554 | public function getSubscriptionDuration() |
||
558 | |||
559 | /** |
||
560 | * Set the subscription duration. |
||
561 | * |
||
562 | * @param string $subscriptionDuration The duration. |
||
563 | * @return $this |
||
564 | */ |
||
565 | 6 | public function setSubscriptionDuration($subscriptionDuration) |
|
571 | |||
572 | /** |
||
573 | * Get the subscription duration follow. |
||
574 | * |
||
575 | * @return string |
||
576 | */ |
||
577 | public function getSubscriptionDurationFollow() |
||
581 | |||
582 | /** |
||
583 | * Set the subscription duration follow. |
||
584 | * |
||
585 | * @param string $subscriptionDurationFollow The duration follow. |
||
586 | * @return $this |
||
587 | */ |
||
588 | 6 | public function setSubscriptionDurationFollow($subscriptionDurationFollow) |
|
594 | |||
595 | /** |
||
596 | * Get the subscription cancellation period. |
||
597 | * @return string |
||
598 | */ |
||
599 | public function getSubscriptionCancellationPeriod() |
||
603 | |||
604 | /** |
||
605 | * Set the subscription cancellation period. |
||
606 | * |
||
607 | * @param string $subscriptionCancellationPeriod The period. |
||
608 | * @return $this |
||
609 | */ |
||
610 | 6 | public function setSubscriptionCancellationPeriod($subscriptionCancellationPeriod) |
|
616 | |||
617 | /** |
||
618 | * Get the success return URL. |
||
619 | * |
||
620 | * @return string |
||
621 | */ |
||
622 | public function getReturnUrlSuccess() |
||
626 | |||
627 | /** |
||
628 | * Set the return success URL. |
||
629 | * |
||
630 | * @param string $returnUrlSuccess The URL. |
||
631 | * @return $this |
||
632 | */ |
||
633 | 6 | public function setReturnUrlSuccess($returnUrlSuccess) |
|
639 | |||
640 | /** |
||
641 | * Get the return cancel URL. |
||
642 | * |
||
643 | * @return string |
||
644 | */ |
||
645 | public function getReturnUrlCancel() |
||
649 | |||
650 | /** |
||
651 | * Set the return cancel URL. |
||
652 | * |
||
653 | * @param string $returnUrlCancel The URL. |
||
654 | * @return $this |
||
655 | */ |
||
656 | 6 | public function setReturnUrlCancel($returnUrlCancel) |
|
662 | |||
663 | /** |
||
664 | * Get the checkout URL. |
||
665 | * |
||
666 | * @return string |
||
667 | */ |
||
668 | 3 | public function getCheckoutUrl() |
|
669 | { |
||
670 | 3 | return $this->checkoutUrl; |
|
671 | } |
||
672 | |||
673 | /** |
||
674 | * Set the checkout URL. |
||
675 | * |
||
676 | * @param string $checkoutUrl The URL. |
||
677 | * @return $this |
||
678 | */ |
||
679 | 6 | public function setCheckoutUrl($checkoutUrl) |
|
685 | |||
686 | /** |
||
687 | * Get the features. |
||
688 | * |
||
689 | * @return array |
||
690 | */ |
||
691 | public function getFeatures() |
||
695 | |||
696 | /** |
||
697 | * Set the features. |
||
698 | * |
||
699 | * @param Feature[] $features The features. |
||
700 | * @return $this |
||
701 | */ |
||
702 | public function setFeatures(array $features) |
||
708 | |||
709 | /** |
||
710 | * Add a feature. |
||
711 | * |
||
712 | * @param Feature $feature The feature to add. |
||
713 | */ |
||
714 | 6 | public function addFeature(Feature $feature) |
|
718 | } |
||
719 |
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.