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 User 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 User, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
39 | class User extends UserAggregateRoot |
||
40 | { |
||
41 | /** |
||
42 | * The id. |
||
43 | * |
||
44 | * @var UserId |
||
45 | */ |
||
46 | protected $id; |
||
47 | |||
48 | /** |
||
49 | * The confirmation token. |
||
50 | * |
||
51 | * @var UserToken |
||
52 | */ |
||
53 | protected $confirmationToken; |
||
54 | |||
55 | /** |
||
56 | * Created on. |
||
57 | * |
||
58 | * @var \DateTimeInterface |
||
59 | */ |
||
60 | protected $createdOn; |
||
61 | |||
62 | /** |
||
63 | * The email. |
||
64 | * |
||
65 | * @var UserEmail |
||
66 | */ |
||
67 | protected $email; |
||
68 | |||
69 | /** |
||
70 | * The invitation token. |
||
71 | * |
||
72 | * @var UserToken |
||
73 | */ |
||
74 | protected $invitationToken; |
||
75 | |||
76 | /** |
||
77 | * The last login. |
||
78 | * |
||
79 | * @var \DateTimeInterface|null |
||
80 | */ |
||
81 | protected $lastLogin; |
||
82 | |||
83 | /** |
||
84 | * The password. |
||
85 | * |
||
86 | * @var UserPassword |
||
87 | */ |
||
88 | protected $password; |
||
89 | |||
90 | /** |
||
91 | * The remember password token. |
||
92 | * |
||
93 | * @var UserToken |
||
94 | */ |
||
95 | protected $rememberPasswordToken; |
||
96 | |||
97 | /** |
||
98 | * Array which contains roles. |
||
99 | * |
||
100 | * @var UserRole[] |
||
101 | */ |
||
102 | protected $roles; |
||
103 | |||
104 | /** |
||
105 | * Updated on. |
||
106 | * |
||
107 | * @var \DateTimeInterface |
||
108 | */ |
||
109 | protected $updatedOn; |
||
110 | |||
111 | /** |
||
112 | * Constructor. |
||
113 | * |
||
114 | * @param UserId $anId The id |
||
115 | * @param UserEmail $anEmail The email |
||
116 | * @param array $userRoles Array which contains the roles |
||
117 | * @param UserPassword|null $aPassword The encoded password |
||
118 | */ |
||
119 | protected function __construct(UserId $anId, UserEmail $anEmail, array $userRoles, UserPassword $aPassword = null) |
||
132 | |||
133 | /** |
||
134 | * Sign up user. |
||
135 | * |
||
136 | * @param UserId $anId The id |
||
137 | * @param UserEmail $anEmail The email |
||
138 | * @param UserPassword $aPassword The encoded password |
||
139 | * @param array $userRoles Array which contains the roles |
||
140 | * |
||
141 | * @return static |
||
142 | */ |
||
143 | View Code Duplication | public static function signUp(UserId $anId, UserEmail $anEmail, UserPassword $aPassword, array $userRoles) |
|
157 | |||
158 | /** |
||
159 | * Invites user. |
||
160 | * |
||
161 | * @param UserId $anId The id |
||
162 | * @param UserEmail $anEmail The email |
||
163 | * @param array $userRoles Array which contains the roles |
||
164 | * |
||
165 | * @return static |
||
166 | */ |
||
167 | View Code Duplication | public static function invite(UserId $anId, UserEmail $anEmail, array $userRoles) |
|
181 | |||
182 | /** |
||
183 | * Gets the id. |
||
184 | * |
||
185 | * @return UserId |
||
186 | */ |
||
187 | public function id() |
||
191 | |||
192 | /** |
||
193 | * Accepts the invitation request. |
||
194 | * |
||
195 | * @throws UserTokenExpiredException when the token is expired |
||
196 | */ |
||
197 | public function acceptInvitation() |
||
214 | |||
215 | /** |
||
216 | * Updates the user password. |
||
217 | * |
||
218 | * @param UserPassword $aPassword The old password |
||
219 | * |
||
220 | * @throws UserTokenExpiredException when the token is expired |
||
221 | */ |
||
222 | public function changePassword(UserPassword $aPassword) |
||
228 | |||
229 | /** |
||
230 | * Cleans the invitation token. |
||
231 | */ |
||
232 | public function cleanInvitationToken() |
||
238 | |||
239 | /** |
||
240 | * Cleans the remember password token. |
||
241 | */ |
||
242 | public function cleanRememberPasswordToken() |
||
248 | |||
249 | /** |
||
250 | * Gets the confirmation token. |
||
251 | * |
||
252 | * @return UserToken|null |
||
253 | */ |
||
254 | public function confirmationToken() |
||
263 | |||
264 | /** |
||
265 | * Gets the created on. |
||
266 | * |
||
267 | * @return \DateTimeInterface |
||
268 | */ |
||
269 | public function createdOn() |
||
273 | |||
274 | /** |
||
275 | * Gets the email. |
||
276 | * |
||
277 | * @return UserEmail |
||
278 | */ |
||
279 | public function email() |
||
283 | |||
284 | /** |
||
285 | * Enables the user account. |
||
286 | */ |
||
287 | public function enableAccount() |
||
299 | |||
300 | /** |
||
301 | * Adds the given role. |
||
302 | * |
||
303 | * @param UserRole $aRole The user role |
||
304 | * |
||
305 | * @throws UserRoleInvalidException when the user is role is invalid |
||
306 | * @throws UserRoleAlreadyGrantedException when the user role is already granted |
||
307 | */ |
||
308 | public function grant(UserRole $aRole) |
||
327 | |||
328 | /** |
||
329 | * Gets the invitation token. |
||
330 | * |
||
331 | * @return UserToken|null |
||
332 | */ |
||
333 | public function invitationToken() |
||
342 | |||
343 | /** |
||
344 | * Checks if the user is enabled or not. |
||
345 | * |
||
346 | * @return bool |
||
347 | */ |
||
348 | public function isEnabled() |
||
352 | |||
353 | /** |
||
354 | * Checks if the user has the given role. |
||
355 | * |
||
356 | * @param UserRole $aRole The user role |
||
357 | * |
||
358 | * @return bool |
||
359 | */ |
||
360 | public function isGranted(UserRole $aRole) |
||
370 | |||
371 | /** |
||
372 | * Checks if the invitation token is accepted or not. |
||
373 | * |
||
374 | * @return bool |
||
375 | */ |
||
376 | public function isInvitationTokenAccepted() |
||
380 | |||
381 | /** |
||
382 | * Checks if the invitation token is expired or not. |
||
383 | * |
||
384 | * @throws UserTokenNotFoundException when the invitation token does not exist |
||
385 | * |
||
386 | * @return bool |
||
387 | */ |
||
388 | public function isInvitationTokenExpired() |
||
398 | |||
399 | /** |
||
400 | * Checks if the remember password token is expired or not. |
||
401 | * |
||
402 | * @throws UserTokenNotFoundException when the remember password token does not exist |
||
403 | * |
||
404 | * @return bool |
||
405 | */ |
||
406 | public function isRememberPasswordTokenExpired() |
||
416 | |||
417 | /** |
||
418 | * Checks if the role given appears between allowed roles. |
||
419 | * |
||
420 | * @param UserRole $aRole The user role |
||
421 | * |
||
422 | * @return bool |
||
423 | */ |
||
424 | public function isRoleAllowed(UserRole $aRole) |
||
428 | |||
429 | /** |
||
430 | * Gets the last login. |
||
431 | * |
||
432 | * @return \DateTimeInterface |
||
433 | */ |
||
434 | public function lastLogin() |
||
438 | |||
439 | /** |
||
440 | * Validates user login for the given password. |
||
441 | * |
||
442 | * @param string $aPlainPassword Plain password used to log in |
||
443 | * @param UserPasswordEncoder $anEncoder The encoder used to encode the password |
||
444 | * |
||
445 | * @throws UserInactiveException when the user is not enabled |
||
446 | * @throws UserPasswordInvalidException when the user password is invalid |
||
447 | */ |
||
448 | public function login($aPlainPassword, UserPasswordEncoder $anEncoder) |
||
465 | |||
466 | /** |
||
467 | * Updated the user state after logout. |
||
468 | * |
||
469 | * @throws UserInactiveException when the user is not enabled |
||
470 | */ |
||
471 | public function logout() |
||
484 | |||
485 | /** |
||
486 | * Gets the password. |
||
487 | * |
||
488 | * @return UserPassword |
||
489 | */ |
||
490 | public function password() |
||
494 | |||
495 | /** |
||
496 | * Updates the invitation token in case a user has |
||
497 | * been already invited and has lost the token. |
||
498 | * |
||
499 | * @throws UserInvitationAlreadyAcceptedException in case user has already accepted the invitation |
||
500 | */ |
||
501 | public function regenerateInvitationToken() |
||
516 | |||
517 | /** |
||
518 | * Gets the remember password token. |
||
519 | * |
||
520 | * @return UserToken |
||
521 | */ |
||
522 | public function rememberPasswordToken() |
||
531 | |||
532 | /** |
||
533 | * Remembers the password. |
||
534 | */ |
||
535 | public function rememberPassword() |
||
547 | |||
548 | /** |
||
549 | * Removes the given role. |
||
550 | * |
||
551 | * @param UserRole $aRole The user role |
||
552 | * |
||
553 | * @throws UserRoleInvalidException when the role is invalid |
||
554 | * @throws UserRoleAlreadyRevokedException when the role is already revoked |
||
555 | */ |
||
556 | public function revoke(UserRole $aRole) |
||
585 | |||
586 | /** |
||
587 | * Gets the roles. |
||
588 | * |
||
589 | * @return UserRole[] |
||
590 | */ |
||
591 | public function roles() |
||
595 | |||
596 | /** |
||
597 | * Gets the updated on. |
||
598 | * |
||
599 | * @return \DateTimeInterface |
||
600 | */ |
||
601 | public function updatedOn() |
||
605 | |||
606 | /** |
||
607 | * Gets the available roles in scalar type. |
||
608 | * |
||
609 | * This method is an extension point that it allows |
||
610 | * to add more roles easily in the domain. |
||
611 | * |
||
612 | * @return array |
||
613 | */ |
||
614 | public static function availableRoles() |
||
618 | |||
619 | /** |
||
620 | * Extension point that determines the lifetime |
||
621 | * of the invitation token in seconds. |
||
622 | * |
||
623 | * @return int |
||
624 | */ |
||
625 | protected function invitationTokenLifetime() |
||
629 | |||
630 | /** |
||
631 | * Extension point that determines the lifetime |
||
632 | * of the remember password token in seconds. |
||
633 | * |
||
634 | * @return int |
||
635 | */ |
||
636 | protected function rememberPasswordTokenLifetime() |
||
640 | } |
||
641 |
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.