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 EnumSet 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 EnumSet, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | class EnumSet implements Iterator, Countable |
||
17 | { |
||
18 | /** |
||
19 | * The classname of the Enumeration |
||
20 | * @var string |
||
21 | */ |
||
22 | private $enumeration; |
||
23 | |||
24 | /** |
||
25 | * BitSet of all attached enumerations in little endian |
||
26 | * @var string |
||
27 | */ |
||
28 | private $bitset; |
||
29 | |||
30 | /** |
||
31 | * Ordinal number of current iterator position |
||
32 | * @var int |
||
33 | */ |
||
34 | private $ordinal = 0; |
||
35 | |||
36 | /** |
||
37 | * Highest possible ordinal number |
||
38 | * @var int |
||
39 | */ |
||
40 | private $ordinalMax; |
||
41 | |||
42 | /** |
||
43 | * Constructor |
||
44 | * |
||
45 | * @param string $enumeration The classname of the enumeration |
||
46 | * @throws InvalidArgumentException |
||
47 | */ |
||
48 | 46 | public function __construct($enumeration) |
|
49 | { |
||
50 | 46 | View Code Duplication | if (!is_subclass_of($enumeration, __NAMESPACE__ . '\Enum')) { |
51 | 1 | throw new InvalidArgumentException(sprintf( |
|
52 | 1 | "This EnumSet can handle subclasses of '%s' only", |
|
53 | __NAMESPACE__ . '\Enum' |
||
54 | 1 | )); |
|
55 | } |
||
56 | |||
57 | 45 | $this->enumeration = $enumeration; |
|
58 | 45 | $this->ordinalMax = count($enumeration::getConstants()); |
|
59 | |||
60 | // init the bitset with zeros |
||
61 | 45 | $this->bitset = str_repeat("\0", ceil($this->ordinalMax / 8)); |
|
62 | 45 | } |
|
63 | |||
64 | /** |
||
65 | * Get the classname of enumeration this set is for |
||
66 | * @return string |
||
67 | * @deprecated Please use getEnumeration() instead |
||
68 | */ |
||
69 | public function getEnumClass() |
||
70 | { |
||
71 | return $this->getEnumeration(); |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Get the classname of the enumeration |
||
76 | * @return string |
||
77 | */ |
||
78 | 12 | public function getEnumeration() |
|
82 | |||
83 | /** |
||
84 | * Attach a new enumerator or overwrite an existing one |
||
85 | * @param Enum|null|boolean|int|float|string $enumerator |
||
86 | * @return void |
||
87 | * @throws InvalidArgumentException On an invalid given enumerator |
||
88 | */ |
||
89 | 35 | public function attach($enumerator) |
|
94 | |||
95 | /** |
||
96 | * Detach the given enumerator |
||
97 | * @param Enum|null|boolean|int|float|string $enumerator |
||
98 | * @return void |
||
99 | * @throws InvalidArgumentException On an invalid given enumerator |
||
100 | */ |
||
101 | 7 | public function detach($enumerator) |
|
106 | |||
107 | /** |
||
108 | * Test if the given enumerator was attached |
||
109 | * @param Enum|null|boolean|int|float|string $enumerator |
||
110 | * @return boolean |
||
111 | */ |
||
112 | 11 | public function contains($enumerator) |
|
117 | |||
118 | /* Iterator */ |
||
119 | |||
120 | /** |
||
121 | * Get the current enumerator |
||
122 | * @return Enum|null Returns the current enumerator or NULL on an invalid iterator position |
||
123 | */ |
||
124 | 9 | public function current() |
|
133 | |||
134 | /** |
||
135 | * Get the ordinal number of the current iterator position |
||
136 | * @return int |
||
137 | */ |
||
138 | 6 | public function key() |
|
142 | |||
143 | /** |
||
144 | * Go to the next valid iterator position. |
||
145 | * If no valid iterator position is found the iterator position will be the last possible + 1. |
||
146 | * @return void |
||
147 | */ |
||
148 | 16 | public function next() |
|
157 | |||
158 | /** |
||
159 | * Go to the first valid iterator position. |
||
160 | * If no valid iterator position in found the iterator position will be 0. |
||
161 | * @return void |
||
162 | */ |
||
163 | 10 | public function rewind() |
|
172 | |||
173 | /** |
||
174 | * Test if the iterator in a valid state |
||
175 | * @return boolean |
||
176 | */ |
||
177 | 10 | public function valid() |
|
181 | |||
182 | /* Countable */ |
||
183 | |||
184 | /** |
||
185 | * Count the number of elements |
||
186 | * @return int |
||
187 | */ |
||
188 | 11 | public function count() |
|
201 | |||
202 | /** |
||
203 | * Check if this EnumSet is the same as other |
||
204 | * @param EnumSet $other |
||
205 | * @return bool |
||
206 | */ |
||
207 | 2 | public function isEqual(EnumSet $other) |
|
212 | |||
213 | /** |
||
214 | * Check if this EnumSet is a subset of other |
||
215 | * @param EnumSet $other |
||
216 | * @return bool |
||
217 | */ |
||
218 | 4 | View Code Duplication | public function isSubset(EnumSet $other) |
227 | |||
228 | /** |
||
229 | * Check if this EnumSet is a superset of other |
||
230 | * @param EnumSet $other |
||
231 | * @return bool |
||
232 | */ |
||
233 | 4 | View Code Duplication | public function isSuperset(EnumSet $other) |
242 | |||
243 | /** |
||
244 | * Produce a new set with enumerators from both this and other (this | other) |
||
245 | * @param EnumSet $other,... Other EnumSet(s) of the same enumeration to produce the union |
||
246 | * @return EnumSet |
||
247 | */ |
||
248 | 1 | View Code Duplication | public function union(EnumSet $other) |
267 | |||
268 | /** |
||
269 | * Produce a new set with enumerators common to both this and other (this & other) |
||
270 | * @param EnumSet $other,... Other EnumSet(s) of the same enumeration to produce the union |
||
271 | * @return EnumSet |
||
272 | */ |
||
273 | 1 | View Code Duplication | public function intersect(EnumSet $other) |
292 | |||
293 | /** |
||
294 | * Produce a new set with enumerators in this but not in other (this - (other | other)) |
||
295 | * @param EnumSet $other,... Other EnumSet(s) of the same enumeration to produce the union |
||
296 | * @return EnumSet |
||
297 | */ |
||
298 | 1 | View Code Duplication | public function diff(EnumSet $other) |
317 | |||
318 | /** |
||
319 | * Produce a new set with enumerators in either this and other but not in both (this ^ (other | other)) |
||
320 | * @param EnumSet $other,... Other EnumSet(s) of the same enumeration to produce the union |
||
321 | * @return EnumSet |
||
322 | */ |
||
323 | 1 | View Code Duplication | public function symDiff(EnumSet $other) |
342 | |||
343 | /** |
||
344 | * Get ordinal numbers of the defined enumerators as array |
||
345 | * @return int[] |
||
346 | */ |
||
347 | 12 | public function getOrdinals() |
|
366 | |||
367 | /** |
||
368 | * Get values of the defined enumerators as array |
||
369 | * @return null[]|bool[]|int[]|float[]|string[] |
||
370 | */ |
||
371 | 6 | public function getValues() |
|
379 | |||
380 | /** |
||
381 | * Get names of the defined enumerators as array |
||
382 | * @return string[] |
||
383 | */ |
||
384 | 2 | public function getNames() |
|
392 | |||
393 | /** |
||
394 | * Get the defined enumerators as array |
||
395 | * @return Enum[] |
||
396 | */ |
||
397 | 10 | public function getEnumerators() |
|
406 | |||
407 | /** |
||
408 | * Get binary bitset in little-endian order |
||
409 | * |
||
410 | * @return string |
||
411 | */ |
||
412 | 10 | public function getBinaryBitsetLe() |
|
416 | |||
417 | /** |
||
418 | * Set binary bitset in little-endian order |
||
419 | * |
||
420 | * NOTE: It resets the current position of the iterator |
||
421 | * |
||
422 | * @param string $bitset |
||
423 | * @return void |
||
424 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
425 | */ |
||
426 | 6 | public function setBinaryBitsetLe($bitset) |
|
446 | |||
447 | /** |
||
448 | * Get binary bitset in big-endian order |
||
449 | * |
||
450 | * @return string |
||
451 | */ |
||
452 | 2 | public function getBinaryBitsetBe() |
|
456 | |||
457 | /** |
||
458 | * Set binary bitset in big-endian order |
||
459 | * |
||
460 | * NOTE: It resets the current position of the iterator |
||
461 | * |
||
462 | * @param string $bitset |
||
463 | * @return void |
||
464 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
465 | */ |
||
466 | 3 | public function setBinaryBitsetBe($bitset) |
|
474 | |||
475 | /** |
||
476 | * Get the binary bitset |
||
477 | * |
||
478 | * @return string Returns the binary bitset in big-endian order |
||
479 | * @deprecated Please use getBinaryBitsetBe() instead |
||
480 | */ |
||
481 | public function getBitset() |
||
485 | |||
486 | /** |
||
487 | * Set the bitset. |
||
488 | * NOTE: It resets the current position of the iterator |
||
489 | * |
||
490 | * @param string $bitset The binary bitset in big-endian order |
||
491 | * @return void |
||
492 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
493 | * @deprecated Please use setBinaryBitsetBe() instead |
||
494 | */ |
||
495 | public function setBitset($bitset) |
||
499 | |||
500 | /** |
||
501 | * Get a bit at the given ordinal number |
||
502 | * |
||
503 | * @param $ordinal int Ordinal number of bit to get |
||
504 | * @return boolean |
||
505 | */ |
||
506 | 22 | private function getBit($ordinal) |
|
510 | |||
511 | /** |
||
512 | * Set a bit at the given ordinal number |
||
513 | * |
||
514 | * @param $ordinal int Ordnal number of bit to set |
||
515 | * @return void |
||
516 | */ |
||
517 | 35 | private function setBit($ordinal) |
|
522 | |||
523 | /** |
||
524 | * Unset a bit at the given ordinal number |
||
525 | * |
||
526 | * @param $ordinal int Ordinal number of bit to unset |
||
527 | * @return void |
||
528 | */ |
||
529 | 7 | private function unsetBit($ordinal) |
|
534 | } |
||
535 |
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.