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 | * Ordinal number of current iterator position |
||
| 26 | * @var int |
||
| 27 | */ |
||
| 28 | private $ordinal = 0; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * Highest possible ordinal number |
||
| 32 | * @var int |
||
| 33 | */ |
||
| 34 | private $ordinalMax; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * Integer or binary (little endian) bitset |
||
| 38 | * @var int|string |
||
| 39 | */ |
||
| 40 | private $bitset = 0; |
||
| 41 | |||
| 42 | /**#@+ |
||
| 43 | * Defined the private method names to be called depended of |
||
| 44 | * how the bitset type was set too. |
||
| 45 | * ... Integer or binary bitset. |
||
| 46 | * ... *Int or *Bin method |
||
| 47 | * |
||
| 48 | * @var string |
||
| 49 | */ |
||
| 50 | private $fnDoRewind = 'doRewindInt'; |
||
| 51 | private $fnDoCount = 'doCountInt'; |
||
| 52 | private $fnDoGetOrdinals = 'doGetOrdinalsInt'; |
||
| 53 | private $fnDoGetBit = 'doGetBitInt'; |
||
| 54 | private $fnDoSetBit = 'doSetBitInt'; |
||
| 55 | private $fnDoUnsetBit = 'doUnsetBitInt'; |
||
| 56 | private $fnDoGetBinaryBitsetLe = 'doGetBinaryBitsetLeInt'; |
||
| 57 | private $fnDoSetBinaryBitsetLe = 'doSetBinaryBitsetLeInt'; |
||
| 58 | /**#@-*/ |
||
| 59 | |||
| 60 | /** |
||
| 61 | * Constructor |
||
| 62 | * |
||
| 63 | * @param string $enumeration The classname of the enumeration |
||
| 64 | * @throws InvalidArgumentException |
||
| 65 | */ |
||
| 66 | 130 | public function __construct($enumeration) |
|
| 97 | |||
| 98 | /** |
||
| 99 | * Get the classname of the enumeration |
||
| 100 | * @return string |
||
| 101 | */ |
||
| 102 | 2 | public function getEnumeration() |
|
| 106 | |||
| 107 | /** |
||
| 108 | * Attach a new enumerator or overwrite an existing one |
||
| 109 | * @param Enum|null|boolean|int|float|string $enumerator |
||
| 110 | * @return void |
||
| 111 | * @throws InvalidArgumentException On an invalid given enumerator |
||
| 112 | */ |
||
| 113 | 82 | public function attach($enumerator) |
|
| 118 | |||
| 119 | /** |
||
| 120 | * Detach the given enumerator |
||
| 121 | * @param Enum|null|boolean|int|float|string $enumerator |
||
| 122 | * @return void |
||
| 123 | * @throws InvalidArgumentException On an invalid given enumerator |
||
| 124 | */ |
||
| 125 | 14 | public function detach($enumerator) |
|
| 130 | |||
| 131 | /** |
||
| 132 | * Test if the given enumerator was attached |
||
| 133 | * @param Enum|null|boolean|int|float|string $enumerator |
||
| 134 | * @return boolean |
||
| 135 | */ |
||
| 136 | 16 | public function contains($enumerator) |
|
| 141 | |||
| 142 | /* Iterator */ |
||
| 143 | |||
| 144 | /** |
||
| 145 | * Get the current enumerator |
||
| 146 | * @return Enum|null Returns the current enumerator or NULL on an invalid iterator position |
||
| 147 | */ |
||
| 148 | 24 | public function current() |
|
| 157 | |||
| 158 | /** |
||
| 159 | * Get the ordinal number of the current iterator position |
||
| 160 | * @return int |
||
| 161 | */ |
||
| 162 | 14 | public function key() |
|
| 166 | |||
| 167 | /** |
||
| 168 | * Go to the next valid iterator position. |
||
| 169 | * If no valid iterator position is found the iterator position will be the last possible + 1. |
||
| 170 | * @return void |
||
| 171 | */ |
||
| 172 | 38 | public function next() |
|
| 181 | |||
| 182 | /** |
||
| 183 | * Go to the first valid iterator position. |
||
| 184 | * If no valid iterator position was found the iterator position will be 0. |
||
| 185 | * @return void |
||
| 186 | * @see doRewindBin |
||
| 187 | * @see doRewindInt |
||
| 188 | */ |
||
| 189 | 24 | public function rewind() |
|
| 193 | |||
| 194 | /** |
||
| 195 | * Go to the first valid iterator position. |
||
| 196 | * If no valid iterator position was found the iterator position will be 0. |
||
| 197 | * |
||
| 198 | * This is the binary bitset implementation. |
||
| 199 | * |
||
| 200 | * @return void |
||
| 201 | * @see rewind |
||
| 202 | * @see doRewindInt |
||
| 203 | */ |
||
| 204 | 10 | private function doRewindBin() |
|
| 205 | { |
||
| 206 | 10 | if (trim($this->bitset, "\0") !== '') { |
|
| 207 | 10 | $this->ordinal = -1; |
|
| 208 | 10 | $this->next(); |
|
| 209 | 5 | } else { |
|
| 210 | 2 | $this->ordinal = 0; |
|
| 211 | } |
||
| 212 | 10 | } |
|
| 213 | |||
| 214 | /** |
||
| 215 | * Go to the first valid iterator position. |
||
| 216 | * If no valid iterator position was found the iterator position will be 0. |
||
| 217 | * |
||
| 218 | * This is the binary bitset implementation. |
||
| 219 | * |
||
| 220 | * @return void |
||
| 221 | * @see rewind |
||
| 222 | * @see doRewindBin |
||
| 223 | */ |
||
| 224 | 14 | private function doRewindInt() |
|
| 233 | |||
| 234 | /** |
||
| 235 | * Test if the iterator in a valid state |
||
| 236 | * @return boolean |
||
| 237 | */ |
||
| 238 | 26 | public function valid() |
|
| 242 | |||
| 243 | /* Countable */ |
||
| 244 | |||
| 245 | /** |
||
| 246 | * Count the number of elements |
||
| 247 | * |
||
| 248 | * @return int |
||
| 249 | * @see doCountBin |
||
| 250 | * @see doCountInt |
||
| 251 | */ |
||
| 252 | 26 | public function count() |
|
| 256 | |||
| 257 | /** |
||
| 258 | * Count the number of elements. |
||
| 259 | * |
||
| 260 | * This is the binary bitset implementation. |
||
| 261 | * |
||
| 262 | * @return int |
||
| 263 | * @see count |
||
| 264 | * @see doCountInt |
||
| 265 | */ |
||
| 266 | 10 | private function doCountBin() |
|
| 285 | |||
| 286 | /** |
||
| 287 | * Count the number of elements. |
||
| 288 | * |
||
| 289 | * This is the integer bitset implementation. |
||
| 290 | * |
||
| 291 | * @return int |
||
| 292 | * @see count |
||
| 293 | * @see doCountBin |
||
| 294 | */ |
||
| 295 | 16 | private function doCountInt() |
|
| 329 | |||
| 330 | /** |
||
| 331 | * Check if this EnumSet is the same as other |
||
| 332 | * @param EnumSet $other |
||
| 333 | * @return bool |
||
| 334 | */ |
||
| 335 | 6 | public function isEqual(EnumSet $other) |
|
| 340 | |||
| 341 | /** |
||
| 342 | * Check if this EnumSet is a subset of other |
||
| 343 | * @param EnumSet $other |
||
| 344 | * @return bool |
||
| 345 | */ |
||
| 346 | 8 | View Code Duplication | public function isSubset(EnumSet $other) |
| 354 | |||
| 355 | /** |
||
| 356 | * Check if this EnumSet is a superset of other |
||
| 357 | * @param EnumSet $other |
||
| 358 | * @return bool |
||
| 359 | */ |
||
| 360 | 8 | View Code Duplication | public function isSuperset(EnumSet $other) |
| 368 | |||
| 369 | /** |
||
| 370 | * Produce a new set with enumerators from both this and other (this | other) |
||
| 371 | * |
||
| 372 | * FIXME: No variadic params with type constraints because of https://github.com/facebook/hhvm/issues/6954 |
||
| 373 | * |
||
| 374 | * @param EnumSet ...$others Other EnumSet(s) of the same enumeration to produce the union |
||
| 375 | * @return EnumSet |
||
| 376 | */ |
||
| 377 | 4 | View Code Duplication | public function union(...$others) |
| 396 | |||
| 397 | /** |
||
| 398 | * Produce a new set with enumerators common to both this and other (this & other) |
||
| 399 | * |
||
| 400 | * FIXME: No variadic params with type constraints because of https://github.com/facebook/hhvm/issues/6954 |
||
| 401 | * |
||
| 402 | * @param EnumSet ...$others Other EnumSet(s) of the same enumeration to produce the union |
||
| 403 | * @return EnumSet |
||
| 404 | */ |
||
| 405 | 4 | View Code Duplication | public function intersect(...$others) |
| 424 | |||
| 425 | /** |
||
| 426 | * Produce a new set with enumerators in this but not in other (this - other) |
||
| 427 | * |
||
| 428 | * FIXME: No variadic params with type constraints because of https://github.com/facebook/hhvm/issues/6954 |
||
| 429 | * |
||
| 430 | * @param EnumSet ...$others Other EnumSet(s) of the same enumeration to produce the union |
||
| 431 | * @return EnumSet |
||
| 432 | */ |
||
| 433 | 4 | View Code Duplication | public function diff(...$others) |
| 456 | |||
| 457 | /** |
||
| 458 | * Produce a new set with enumerators in either this and other but not in both (this ^ (other | other)) |
||
| 459 | * |
||
| 460 | * FIXME: No variadic params with type constraints because of https://github.com/facebook/hhvm/issues/6954 |
||
| 461 | * |
||
| 462 | * @param EnumSet ...$others Other EnumSet(s) of the same enumeration to produce the union |
||
| 463 | * @return EnumSet |
||
| 464 | */ |
||
| 465 | 4 | View Code Duplication | public function symDiff(...$others) |
| 488 | |||
| 489 | /** |
||
| 490 | * Get ordinal numbers of the defined enumerators as array |
||
| 491 | * @return int[] |
||
| 492 | */ |
||
| 493 | 28 | public function getOrdinals() |
|
| 497 | |||
| 498 | /** |
||
| 499 | * Get ordinal numbers of the defined enumerators as array. |
||
| 500 | * |
||
| 501 | * This is the binary bitset implementation. |
||
| 502 | * |
||
| 503 | * @return int[] |
||
| 504 | * @see getOrdinals |
||
| 505 | * @see goGetOrdinalsInt |
||
| 506 | */ |
||
| 507 | 4 | private function doGetOrdinalsBin() |
|
| 526 | |||
| 527 | /** |
||
| 528 | * Get ordinal numbers of the defined enumerators as array. |
||
| 529 | * |
||
| 530 | * This is the integer bitset implementation. |
||
| 531 | * |
||
| 532 | * @return int[] |
||
| 533 | * @see getOrdinals |
||
| 534 | * @see doGetOrdinalsBin |
||
| 535 | */ |
||
| 536 | 24 | private function doGetOrdinalsInt() |
|
| 546 | |||
| 547 | /** |
||
| 548 | * Get values of the defined enumerators as array |
||
| 549 | * @return null[]|bool[]|int[]|float[]|string[] |
||
| 550 | */ |
||
| 551 | 12 | public function getValues() |
|
| 560 | |||
| 561 | /** |
||
| 562 | * Get names of the defined enumerators as array |
||
| 563 | * @return string[] |
||
| 564 | */ |
||
| 565 | 4 | public function getNames() |
|
| 574 | |||
| 575 | /** |
||
| 576 | * Get the defined enumerators as array |
||
| 577 | * @return Enum[] |
||
| 578 | */ |
||
| 579 | 4 | public function getEnumerators() |
|
| 588 | |||
| 589 | /** |
||
| 590 | * Get binary bitset in little-endian order |
||
| 591 | * |
||
| 592 | * @return string |
||
| 593 | */ |
||
| 594 | 12 | public function getBinaryBitsetLe() |
|
| 598 | |||
| 599 | /** |
||
| 600 | * Get binary bitset in little-endian order. |
||
| 601 | * |
||
| 602 | * This is the binary bitset implementation. |
||
| 603 | * |
||
| 604 | * @return string |
||
| 605 | */ |
||
| 606 | 8 | private function doGetBinaryBitsetLeBin() |
|
| 610 | |||
| 611 | /** |
||
| 612 | * Get binary bitset in little-endian order. |
||
| 613 | * |
||
| 614 | * This is the integer bitset implementation. |
||
| 615 | * |
||
| 616 | * @return string |
||
| 617 | */ |
||
| 618 | 4 | private function doGetBinaryBitsetLeInt() |
|
| 623 | |||
| 624 | /** |
||
| 625 | * Set binary bitset in little-endian order |
||
| 626 | * |
||
| 627 | * NOTE: It resets the current position of the iterator |
||
| 628 | * |
||
| 629 | * @param string $bitset |
||
| 630 | * @return void |
||
| 631 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
| 632 | */ |
||
| 633 | 26 | public function setBinaryBitsetLe($bitset) |
|
| 644 | |||
| 645 | /** |
||
| 646 | * Set binary bitset in little-endian order |
||
| 647 | * |
||
| 648 | * NOTE: It resets the current position of the iterator |
||
| 649 | * |
||
| 650 | * @param string $bitset |
||
| 651 | * @return void |
||
| 652 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
| 653 | */ |
||
| 654 | 14 | private function doSetBinaryBitsetLeBin($bitset) |
|
| 683 | |||
| 684 | /** |
||
| 685 | * Set binary bitset in little-endian order |
||
| 686 | * |
||
| 687 | * NOTE: It resets the current position of the iterator |
||
| 688 | * |
||
| 689 | * @param string $bitset |
||
| 690 | * @return void |
||
| 691 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
| 692 | */ |
||
| 693 | 10 | private function doSetBinaryBitsetLeInt($bitset) |
|
| 713 | |||
| 714 | /** |
||
| 715 | * Get binary bitset in big-endian order |
||
| 716 | * |
||
| 717 | * @return string |
||
| 718 | */ |
||
| 719 | 2 | public function getBinaryBitsetBe() |
|
| 723 | |||
| 724 | /** |
||
| 725 | * Set binary bitset in big-endian order |
||
| 726 | * |
||
| 727 | * NOTE: It resets the current position of the iterator |
||
| 728 | * |
||
| 729 | * @param string $bitset |
||
| 730 | * @return void |
||
| 731 | * @throws InvalidArgumentException On a non string is given as Parameter |
||
| 732 | */ |
||
| 733 | 4 | public function setBinaryBitsetBe($bitset) |
|
| 740 | |||
| 741 | /** |
||
| 742 | * Get a bit at the given ordinal number |
||
| 743 | * |
||
| 744 | * @param int $ordinal Ordinal number of bit to get |
||
| 745 | * @return boolean |
||
| 746 | */ |
||
| 747 | 6 | public function getBit($ordinal) |
|
| 755 | |||
| 756 | /** |
||
| 757 | * Get a bit at the given ordinal number. |
||
| 758 | * |
||
| 759 | * This is the binary bitset implementation. |
||
| 760 | * |
||
| 761 | * @param int $ordinal Ordinal number of bit to get |
||
| 762 | * @return boolean |
||
| 763 | * @see getBit |
||
| 764 | * @see doGetBitInt |
||
| 765 | */ |
||
| 766 | 18 | private function doGetBitBin($ordinal) |
|
| 770 | |||
| 771 | /** |
||
| 772 | * Get a bit at the given ordinal number. |
||
| 773 | * |
||
| 774 | * This is the integer bitset implementation. |
||
| 775 | * |
||
| 776 | * @param int $ordinal Ordinal number of bit to get |
||
| 777 | * @return boolean |
||
| 778 | * @see getBit |
||
| 779 | * @see doGetBitBin |
||
| 780 | */ |
||
| 781 | 32 | private function doGetBitInt($ordinal) |
|
| 785 | |||
| 786 | /** |
||
| 787 | * Set a bit at the given ordinal number |
||
| 788 | * |
||
| 789 | * @param int $ordinal Ordnal number of bit to set |
||
| 790 | * @param bool $bit The bit to set |
||
| 791 | * @return void |
||
| 792 | * @see doSetBitBin |
||
| 793 | * @see doSetBitInt |
||
| 794 | * @see doUnsetBin |
||
| 795 | * @see doUnsetInt |
||
| 796 | */ |
||
| 797 | 4 | public function setBit($ordinal, $bit) |
|
| 809 | |||
| 810 | /** |
||
| 811 | * Set a bit at the given ordinal number. |
||
| 812 | * |
||
| 813 | * This is the binary bitset implementation. |
||
| 814 | * |
||
| 815 | * @param int $ordinal Ordnal number of bit to set |
||
| 816 | * @return void |
||
| 817 | * @see setBit |
||
| 818 | * @see doSetBitInt |
||
| 819 | */ |
||
| 820 | 14 | private function doSetBitBin($ordinal) |
|
| 825 | |||
| 826 | /** |
||
| 827 | * Set a bit at the given ordinal number. |
||
| 828 | * |
||
| 829 | * This is the binary bitset implementation. |
||
| 830 | * |
||
| 831 | * @param int $ordinal Ordnal number of bit to set |
||
| 832 | * @return void |
||
| 833 | * @see setBit |
||
| 834 | * @see doSetBitBin |
||
| 835 | */ |
||
| 836 | 70 | private function doSetBitInt($ordinal) |
|
| 840 | |||
| 841 | /** |
||
| 842 | * Unset a bit at the given ordinal number. |
||
| 843 | * |
||
| 844 | * This is the binary bitset implementation. |
||
| 845 | * |
||
| 846 | * @param int $ordinal Ordinal number of bit to unset |
||
| 847 | * @return void |
||
| 848 | * @see setBit |
||
| 849 | * @see doUnsetBitInt |
||
| 850 | */ |
||
| 851 | 6 | private function doUnsetBitBin($ordinal) |
|
| 856 | |||
| 857 | /** |
||
| 858 | * Unset a bit at the given ordinal number. |
||
| 859 | * |
||
| 860 | * This is the integer bitset implementation. |
||
| 861 | * |
||
| 862 | * @param int $ordinal Ordinal number of bit to unset |
||
| 863 | * @return void |
||
| 864 | * @see setBit |
||
| 865 | * @see doUnsetBitBin |
||
| 866 | */ |
||
| 867 | 10 | private function doUnsetBitInt($ordinal) |
|
| 871 | } |
||
| 872 |
Let’s take a look at an example: