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 B2bCustomer 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 B2bCustomer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
72 | class B2bCustomer extends ExtendB2bCustomer implements |
||
73 | ChannelAwareInterface, |
||
74 | CustomerIdentityInterface |
||
75 | { |
||
76 | use ChannelEntityTrait; |
||
77 | |||
78 | /** |
||
79 | * @var integer |
||
80 | * |
||
81 | * @ORM\Column(name="id", type="integer") |
||
82 | * @ORM\Id |
||
83 | * @ORM\GeneratedValue(strategy="AUTO") |
||
84 | * @ConfigField( |
||
85 | * defaultValues={ |
||
86 | * "importexport"={ |
||
87 | * "order"=0 |
||
88 | * } |
||
89 | * } |
||
90 | * ) |
||
91 | */ |
||
92 | protected $id; |
||
93 | |||
94 | /** |
||
95 | * @var string |
||
96 | * |
||
97 | * @ORM\Column(type="string", length=255) |
||
98 | * @ConfigField( |
||
99 | * defaultValues={ |
||
100 | * "dataaudit"={ |
||
101 | * "auditable"=true |
||
102 | * }, |
||
103 | * "importexport"={ |
||
104 | * "identity"=true, |
||
105 | * "order"=10 |
||
106 | * } |
||
107 | * } |
||
108 | * ) |
||
109 | */ |
||
110 | protected $name; |
||
111 | |||
112 | /** |
||
113 | * @var double |
||
114 | * |
||
115 | * @ORM\Column(name="lifetime", type="money", nullable=true) |
||
116 | * @ConfigField( |
||
117 | * defaultValues={ |
||
118 | * "dataaudit"={ |
||
119 | * "auditable"=true |
||
120 | * }, |
||
121 | * "importexport"={ |
||
122 | * "full"=true, |
||
123 | * "order"=15 |
||
124 | * } |
||
125 | * } |
||
126 | * ) |
||
127 | */ |
||
128 | protected $lifetime = 0; |
||
129 | |||
130 | /** |
||
131 | * @var Address $shippingAddress |
||
132 | * |
||
133 | * @ORM\ManyToOne(targetEntity="Oro\Bundle\AddressBundle\Entity\Address", cascade={"persist", "remove"}) |
||
134 | * @ORM\JoinColumn(name="shipping_address_id", referencedColumnName="id", onDelete="SET NULL") |
||
135 | * @ConfigField( |
||
136 | * defaultValues={ |
||
137 | * "importexport"={ |
||
138 | * "full"=true, |
||
139 | * "order"=20 |
||
140 | * } |
||
141 | * } |
||
142 | * ) |
||
143 | */ |
||
144 | protected $shippingAddress; |
||
145 | |||
146 | /** |
||
147 | * @var Address $billingAddress |
||
148 | * |
||
149 | * @ORM\ManyToOne(targetEntity="Oro\Bundle\AddressBundle\Entity\Address", cascade={"persist", "remove"}) |
||
150 | * @ORM\JoinColumn(name="billing_address_id", referencedColumnName="id", onDelete="SET NULL") |
||
151 | * @ConfigField( |
||
152 | * defaultValues={ |
||
153 | * "importexport"={ |
||
154 | * "full"=true, |
||
155 | * "order"=30 |
||
156 | * } |
||
157 | * } |
||
158 | * ) |
||
159 | */ |
||
160 | protected $billingAddress; |
||
161 | |||
162 | /** |
||
163 | * @var Account |
||
164 | * |
||
165 | * @ORM\ManyToOne(targetEntity="Oro\Bundle\AccountBundle\Entity\Account", cascade="persist") |
||
166 | * @ORM\JoinColumn(name="account_id", referencedColumnName="id", onDelete="SET NULL") |
||
167 | * @ConfigField( |
||
168 | * defaultValues={ |
||
169 | * "dataaudit"={"auditable"=true}, |
||
170 | * "importexport"={ |
||
171 | * "order"=40, |
||
172 | * "short"=true |
||
173 | * } |
||
174 | * } |
||
175 | * ) |
||
176 | */ |
||
177 | protected $account; |
||
178 | |||
179 | /** |
||
180 | * @var Contact |
||
181 | * |
||
182 | * @ORM\ManyToOne(targetEntity="Oro\Bundle\ContactBundle\Entity\Contact") |
||
183 | * @ORM\JoinColumn(name="contact_id", referencedColumnName="id", onDelete="SET NULL") |
||
184 | * @ConfigField( |
||
185 | * defaultValues={ |
||
186 | * "dataaudit"={"auditable"=true}, |
||
187 | * "importexport"={ |
||
188 | * "order"=50, |
||
189 | * "short"=true |
||
190 | * } |
||
191 | * } |
||
192 | * ) |
||
193 | */ |
||
194 | protected $contact; |
||
195 | |||
196 | /** |
||
197 | * @var ArrayCollection |
||
198 | * |
||
199 | * @ORM\OneToMany(targetEntity="Oro\Bundle\SalesBundle\Entity\Lead", mappedBy="customer", cascade={"remove"}) |
||
200 | */ |
||
201 | protected $leads; |
||
202 | |||
203 | /** |
||
204 | * @var ArrayCollection |
||
205 | * |
||
206 | * @ORM\OneToMany( |
||
207 | * targetEntity="Oro\Bundle\SalesBundle\Entity\Opportunity", |
||
208 | * mappedBy="customer", |
||
209 | * cascade={"remove"} |
||
210 | * ) |
||
211 | */ |
||
212 | protected $opportunities; |
||
213 | |||
214 | /** |
||
215 | * @var User |
||
216 | * |
||
217 | * @ORM\ManyToOne(targetEntity="Oro\Bundle\UserBundle\Entity\User") |
||
218 | * @ORM\JoinColumn(name="user_owner_id", referencedColumnName="id", onDelete="SET NULL") |
||
219 | * @ConfigField( |
||
220 | * defaultValues={ |
||
221 | * "dataaudit"={"auditable"=true}, |
||
222 | * "importexport"={ |
||
223 | * "order"=70, |
||
224 | * "short"=true |
||
225 | * } |
||
226 | * } |
||
227 | * ) |
||
228 | */ |
||
229 | protected $owner; |
||
230 | |||
231 | /** |
||
232 | * @var Organization |
||
233 | * |
||
234 | * @ORM\ManyToOne(targetEntity="Oro\Bundle\OrganizationBundle\Entity\Organization") |
||
235 | * @ORM\JoinColumn(name="organization_id", referencedColumnName="id", onDelete="SET NULL") |
||
236 | */ |
||
237 | protected $organization; |
||
238 | |||
239 | /** |
||
240 | * @var \DateTime $created |
||
241 | * |
||
242 | * @ORM\Column(type="datetime") |
||
243 | * @ConfigField( |
||
244 | * defaultValues={ |
||
245 | * "entity"={ |
||
246 | * "label"="oro.ui.created_at" |
||
247 | * }, |
||
248 | * "importexport"={ |
||
249 | * "excluded"=true |
||
250 | * } |
||
251 | * } |
||
252 | * ) |
||
253 | */ |
||
254 | protected $createdAt; |
||
255 | |||
256 | /** |
||
257 | * @var \DateTime $updated |
||
258 | * |
||
259 | * @ORM\Column(type="datetime") |
||
260 | * @ConfigField( |
||
261 | * defaultValues={ |
||
262 | * "entity"={ |
||
263 | * "label"="oro.ui.updated_at" |
||
264 | * }, |
||
265 | * "importexport"={ |
||
266 | * "excluded"=true |
||
267 | * } |
||
268 | * } |
||
269 | * ) |
||
270 | */ |
||
271 | protected $updatedAt; |
||
272 | |||
273 | /** |
||
274 | * @var Collection |
||
275 | * |
||
276 | * @ORM\OneToMany(targetEntity="Oro\Bundle\SalesBundle\Entity\B2bCustomerPhone", mappedBy="owner", |
||
277 | * mappedBy="owner", cascade={"all"}, orphanRemoval=true |
||
278 | * )) |
||
279 | * @ORM\OrderBy({"primary" = "DESC"}) |
||
280 | * @ConfigField( |
||
281 | * defaultValues={ |
||
282 | * "importexport"={ |
||
283 | * "order"=80 |
||
284 | * }, |
||
285 | * "dataaudit"={ |
||
286 | * "auditable"=true |
||
287 | * } |
||
288 | * } |
||
289 | * ) |
||
290 | */ |
||
291 | protected $phones; |
||
292 | |||
293 | /** |
||
294 | * @var Collection |
||
295 | * |
||
296 | * @ORM\OneToMany(targetEntity="Oro\Bundle\SalesBundle\Entity\B2bCustomerEmail", |
||
297 | * mappedBy="owner", cascade={"all"}, orphanRemoval=true |
||
298 | * ) |
||
299 | * @ORM\OrderBy({"primary" = "DESC"}) |
||
300 | * @ConfigField( |
||
301 | * defaultValues={ |
||
302 | * "importexport"={ |
||
303 | * "order"=90 |
||
304 | * }, |
||
305 | * "dataaudit"={ |
||
306 | * "auditable"=true |
||
307 | * } |
||
308 | * } |
||
309 | * ) |
||
310 | */ |
||
311 | protected $emails; |
||
312 | |||
313 | View Code Duplication | public function __construct() |
|
322 | |||
323 | /** |
||
324 | * @return int |
||
325 | */ |
||
326 | public function getId() |
||
330 | |||
331 | /** |
||
332 | * @return string |
||
333 | */ |
||
334 | public function getName() |
||
338 | |||
339 | /** |
||
340 | * @param string $name |
||
341 | */ |
||
342 | public function setName($name) |
||
346 | |||
347 | /** |
||
348 | * @return float |
||
349 | */ |
||
350 | public function getLifetime() |
||
354 | |||
355 | /** |
||
356 | * @param float $lifetime |
||
357 | */ |
||
358 | public function setLifetime($lifetime) |
||
362 | |||
363 | /** |
||
364 | * @return Address |
||
365 | */ |
||
366 | public function getShippingAddress() |
||
370 | |||
371 | /** |
||
372 | * @param Address|null $shippingAddress |
||
373 | */ |
||
374 | public function setShippingAddress(Address $shippingAddress = null) |
||
378 | |||
379 | /** |
||
380 | * @return Address |
||
381 | */ |
||
382 | public function getBillingAddress() |
||
386 | |||
387 | /** |
||
388 | * @param Address|null $billingAddress |
||
389 | */ |
||
390 | public function setBillingAddress(Address $billingAddress = null) |
||
394 | |||
395 | /** |
||
396 | * @return Account |
||
397 | */ |
||
398 | public function getAccount() |
||
402 | |||
403 | /** |
||
404 | * @param Account|null $account |
||
405 | */ |
||
406 | public function setAccount(Account $account = null) |
||
410 | |||
411 | /** |
||
412 | * @return Contact |
||
413 | */ |
||
414 | public function getContact() |
||
418 | |||
419 | /** |
||
420 | * @param Contact|null $contact |
||
421 | */ |
||
422 | public function setContact(Contact $contact = null) |
||
426 | |||
427 | /** |
||
428 | * @return ArrayCollection |
||
429 | */ |
||
430 | public function getLeads() |
||
434 | |||
435 | /** |
||
436 | * @param ArrayCollection $leads |
||
437 | */ |
||
438 | public function setLeads(ArrayCollection $leads) |
||
442 | |||
443 | /** |
||
444 | * @param Lead $lead |
||
445 | */ |
||
446 | public function addLead(Lead $lead) |
||
453 | |||
454 | /** |
||
455 | * @param Lead $lead |
||
456 | */ |
||
457 | public function removeLead(Lead $lead) |
||
464 | |||
465 | /** |
||
466 | * @return ArrayCollection |
||
467 | */ |
||
468 | public function getOpportunities() |
||
472 | |||
473 | /** |
||
474 | * @param ArrayCollection $opportunities |
||
475 | */ |
||
476 | public function setOpportunities(ArrayCollection $opportunities) |
||
480 | |||
481 | /** |
||
482 | * @param Opportunity $opportunity |
||
483 | */ |
||
484 | public function addOpportunity(Opportunity $opportunity) |
||
491 | |||
492 | /** |
||
493 | * @param Opportunity $opportunity |
||
494 | */ |
||
495 | public function removeOpportunity(Opportunity $opportunity) |
||
502 | |||
503 | /** |
||
504 | * @return mixed |
||
505 | */ |
||
506 | public function getOwner() |
||
510 | |||
511 | /** |
||
512 | * @param User $owner |
||
513 | */ |
||
514 | public function setOwner(User $owner = null) |
||
518 | |||
519 | /** |
||
520 | * @return \DateTime |
||
521 | */ |
||
522 | public function getCreatedAt() |
||
526 | |||
527 | /** |
||
528 | * @param \DateTime|null $createdAt |
||
529 | */ |
||
530 | public function setCreatedAt(\DateTime $createdAt = null) |
||
534 | |||
535 | /** |
||
536 | * @return \DateTime |
||
537 | */ |
||
538 | public function getUpdatedAt() |
||
542 | |||
543 | /** |
||
544 | * @param \DateTime|null $updatedAt |
||
545 | */ |
||
546 | public function setUpdatedAt(\DateTime $updatedAt = null) |
||
550 | |||
551 | /** |
||
552 | * Pre persist event listener |
||
553 | * |
||
554 | * @ORM\PrePersist |
||
555 | */ |
||
556 | public function prePersist() |
||
560 | |||
561 | /** |
||
562 | * Pre update event handler |
||
563 | * |
||
564 | * @ORM\PreUpdate |
||
565 | */ |
||
566 | public function preUpdate() |
||
570 | |||
571 | /** |
||
572 | * @return string |
||
573 | */ |
||
574 | public function __toString() |
||
578 | |||
579 | /** |
||
580 | * Set organization |
||
581 | * |
||
582 | * @param Organization $organization |
||
583 | * @return B2bCustomer |
||
584 | */ |
||
585 | public function setOrganization(Organization $organization = null) |
||
591 | |||
592 | /** |
||
593 | * Get organization |
||
594 | * |
||
595 | * @return Organization |
||
596 | */ |
||
597 | public function getOrganization() |
||
601 | |||
602 | /** |
||
603 | * Set phones. |
||
604 | * |
||
605 | * This method could not be named setPhones because of bug CRM-253. |
||
606 | * |
||
607 | * @param Collection|B2bCustomerPhone[] $phones |
||
608 | * |
||
609 | * @return B2bCustomer |
||
610 | */ |
||
611 | public function resetPhones($phones) |
||
619 | /** |
||
620 | * Add phone |
||
621 | * |
||
622 | * @param B2bCustomerPhone $phone |
||
623 | * |
||
624 | * @return B2bCustomer |
||
625 | */ |
||
626 | View Code Duplication | public function addPhone(B2bCustomerPhone $phone) |
|
634 | /** |
||
635 | * Remove phone |
||
636 | * |
||
637 | * @param B2bCustomerPhone $phone |
||
638 | * |
||
639 | * @return B2bCustomer |
||
640 | */ |
||
641 | public function removePhone(B2bCustomerPhone $phone) |
||
648 | /** |
||
649 | * Get phones |
||
650 | * |
||
651 | * @return Collection|B2bCustomerPhone[] |
||
652 | */ |
||
653 | public function getPhones() |
||
657 | /** |
||
658 | * @param B2bCustomerPhone $phone |
||
659 | * |
||
660 | * @return bool |
||
661 | */ |
||
662 | public function hasPhone(B2bCustomerPhone $phone) |
||
666 | /** |
||
667 | * Gets primary phone if it's available. |
||
668 | * |
||
669 | * @return B2bCustomerPhone|null |
||
670 | */ |
||
671 | public function getPrimaryPhone() |
||
682 | /** |
||
683 | * @param B2bCustomerPhone $phone |
||
684 | * |
||
685 | * @return B2bCustomer |
||
686 | */ |
||
687 | View Code Duplication | public function setPrimaryPhone(B2bCustomerPhone $phone) |
|
699 | |||
700 | /** |
||
701 | * Set emails. |
||
702 | * |
||
703 | * This method could not be named setEmails because of bug CRM-253. |
||
704 | * |
||
705 | * @param Collection|B2bCustomerEmail[] $emails |
||
706 | * |
||
707 | * @return B2bCustomer |
||
708 | */ |
||
709 | public function resetEmails($emails) |
||
717 | /** |
||
718 | * Add email |
||
719 | * |
||
720 | * @param B2bCustomerEmail $email |
||
721 | * |
||
722 | * @return B2bCustomer |
||
723 | */ |
||
724 | View Code Duplication | public function addEmail(B2bCustomerEmail $email) |
|
732 | /** |
||
733 | * Remove email |
||
734 | * |
||
735 | * @param B2bCustomerEmail $email |
||
736 | * |
||
737 | * @return B2bCustomer |
||
738 | */ |
||
739 | public function removeEmail(B2bCustomerEmail $email) |
||
746 | /** |
||
747 | * Get emails |
||
748 | * |
||
749 | * @return Collection|B2bCustomerEmail[] |
||
750 | */ |
||
751 | public function getEmails() |
||
755 | /** |
||
756 | * @param B2bCustomerEmail $email |
||
757 | * |
||
758 | * @return bool |
||
759 | */ |
||
760 | public function hasEmail(B2bCustomerEmail $email) |
||
764 | /** |
||
765 | * Gets primary email if it's available. |
||
766 | * |
||
767 | * @return B2bCustomerEmail|null |
||
768 | */ |
||
769 | public function getPrimaryEmail() |
||
780 | |||
781 | /** |
||
782 | * @param B2bCustomerEmail $email |
||
783 | * |
||
784 | * @return B2bCustomer |
||
785 | */ |
||
786 | View Code Duplication | public function setPrimaryEmail(B2bCustomerEmail $email) |
|
798 | } |
||
799 |
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.