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 ContactInfo 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 ContactInfo, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
7 | class ContactInfo extends Model |
||
8 | { |
||
9 | |||
10 | /** |
||
11 | * Get the user's preferred SMS number. |
||
12 | * |
||
13 | * @return Phone|null |
||
14 | */ |
||
15 | public function getSmsNumber() |
||
26 | |||
27 | /** |
||
28 | * Remove the preferred SMS flag from any number. |
||
29 | */ |
||
30 | public function unsetSmsNumber() |
||
40 | |||
41 | /** |
||
42 | * Set the user's preferred SMS number, creating a new internal mobile number if needed |
||
43 | * @param $number string The SMS-capable mobile phone number |
||
44 | */ |
||
45 | public function setSmsNumber($number) |
||
62 | |||
63 | /** |
||
64 | * Add the user's preferred SMS number as a new internal mobile number. |
||
65 | * @param $number string The SMS-capable mobile phone number |
||
66 | * |
||
67 | * @return Phone |
||
68 | */ |
||
69 | public function addSmsNumber($number) |
||
91 | |||
92 | /** |
||
93 | * Adds a new internal phone number to the user |
||
94 | * |
||
95 | * @param string $phone_number The phone number |
||
96 | * @param string $phone_type Type of the phone number (home, mobile, etc.) |
||
97 | * @param bool $preferred Whether this should be the user's preferred phone number |
||
98 | * |
||
99 | * @return Phone |
||
100 | */ |
||
101 | View Code Duplication | public function addPhone($phone_number, $phone_type, $preferred = false) |
|
121 | |||
122 | /** |
||
123 | * Gets the user's preferred phone number, or null if none are preferred |
||
124 | * |
||
125 | * @return Phone|null |
||
126 | */ |
||
127 | public function getPreferredPhone() |
||
136 | |||
137 | /** |
||
138 | * Remove the preferred flag from all phone numbers |
||
139 | */ |
||
140 | public function unsetPreferredPhone() |
||
148 | |||
149 | /** |
||
150 | * Sets the given phone number as the user's preferred number, adding it as an internal home phone if necessary |
||
151 | * |
||
152 | * @param string $phone_number The phone number |
||
153 | * |
||
154 | * @throws Exception when the given phone number is not found in the user |
||
155 | */ |
||
156 | public function setPreferredPhone($phone_number) |
||
171 | |||
172 | /** |
||
173 | * Removes a phone number from the user |
||
174 | * |
||
175 | * @param string The phone number |
||
176 | */ |
||
177 | public function removePhone($phone_number) |
||
186 | |||
187 | /** |
||
188 | * Returns an array of all phone numbers associated with the user |
||
189 | * |
||
190 | * @return array An array of Phone objects |
||
191 | */ |
||
192 | public function allPhones() |
||
200 | |||
201 | /** |
||
202 | * Gets the user's preferred email address |
||
203 | * @return Email The email address |
||
204 | */ |
||
205 | public function getEmail() |
||
216 | |||
217 | /** |
||
218 | * Sets the user's preferred email address, adding a new email address if needed. |
||
219 | * @param string $email_address The email address |
||
220 | * |
||
221 | * @throws Exception when the given email address is not found in the user |
||
222 | */ |
||
223 | public function setEmail($email_address) |
||
240 | |||
241 | /** |
||
242 | * Removes the preferred flag from all email addresses |
||
243 | */ |
||
244 | public function unsetEmail() |
||
254 | |||
255 | /** |
||
256 | * Adds a new email address |
||
257 | * @param string $email_address The email address |
||
258 | * @param string $email_type The email type, defaults to 'personal' |
||
259 | * @param bool $preferred True if this should be the preferred email |
||
260 | * |
||
261 | * @return Email |
||
262 | */ |
||
263 | View Code Duplication | public function addEmail($email_address, $email_type = 'personal', $preferred = false) |
|
283 | |||
284 | /** |
||
285 | * Removes the given email address from the user |
||
286 | * |
||
287 | * @param string $email_address The email address to remove |
||
288 | */ |
||
289 | public function removeEmail($email_address) |
||
298 | |||
299 | /** |
||
300 | * Returns an array of all email addresses associated with the user |
||
301 | * |
||
302 | * @return array An array of Email objects |
||
303 | */ |
||
304 | public function allEmails() |
||
312 | |||
313 | /** |
||
314 | * Get an array of objects representing the user's addresses |
||
315 | * |
||
316 | * @return array An array of Address objects |
||
317 | */ |
||
318 | public function getAddresses() { |
||
325 | |||
326 | /** |
||
327 | * Adds a new address. |
||
328 | * |
||
329 | * @param array $address The address' properties |
||
330 | * @return Address A new address object based on the given values |
||
331 | */ |
||
332 | public function addAddress($address) |
||
351 | |||
352 | /** |
||
353 | * Removes an address from the user |
||
354 | * |
||
355 | * @param Address|stdClass $address Either the Address object to be removed or its underlying stdClass object |
||
356 | */ |
||
357 | public function removeAddress($address) |
||
366 | |||
367 | /** |
||
368 | * Returns the user's preferred address |
||
369 | * |
||
370 | * @return Address|null The address object or null if none are preferred |
||
371 | */ |
||
372 | public function getPreferredAddress() |
||
381 | |||
382 | /** |
||
383 | * Removes the preferred flag from all addresses |
||
384 | */ |
||
385 | public function unsetPreferredAddress() |
||
393 | } |
||
394 |
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.