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 Numbers 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 Numbers, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 12 | class Numbers |
||
| 13 | {
|
||
| 14 | |||
| 15 | const MUTABLE = MutableNumber::class; |
||
| 16 | const IMMUTABLE = ImmutableNumber::class; |
||
| 17 | const MUTABLE_FRACTION = MutableFraction::class; |
||
| 18 | const IMMUTABLE_FRACTION = ImmutableFraction::class; |
||
| 19 | /* 105 digits after decimal, which is going to be overkill in almost all places */ |
||
| 20 | const PI = '3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148'; |
||
| 21 | /* Tau (2pi) to 100 digits */ |
||
| 22 | const TAU = '6.283185307179586476925286766559005768394338798750211641949889184615632812572417997256069650684234136'; |
||
| 23 | /* Euler's Number to 100 digits */ |
||
| 24 | const E = '2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427'; |
||
| 25 | /* Golden Ratio to 100 digits */ |
||
| 26 | const GOLDEN_RATIO = '1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137'; |
||
| 27 | |||
| 28 | /** |
||
| 29 | * @param $type |
||
| 30 | * @param $value |
||
| 31 | * @param int|null $precision |
||
| 32 | * @param int $base |
||
| 33 | * @return ImmutableNumber|MutableNumber|ImmutableFraction|MutableFraction|NumberInterface |
||
| 34 | */ |
||
| 35 | public static function make($type, $value, $precision = null, $base = 10) |
||
| 62 | |||
| 63 | /** |
||
| 64 | * @param $type |
||
| 65 | * @param $value |
||
| 66 | * @param int|null $precision |
||
| 67 | * @param int $base |
||
| 68 | * @return NumberInterface |
||
| 69 | */ |
||
| 70 | public static function makeFromBase10($type, $value, $precision = null, $base = 10) |
||
| 79 | |||
| 80 | /** |
||
| 81 | * @param $type |
||
| 82 | * @param int|float|string|NumberInterface $value |
||
| 83 | * @param int|null $precision |
||
| 84 | * @param int $base |
||
| 85 | * |
||
| 86 | *@throws \InvalidArgumentException |
||
| 87 | * @return ImmutableNumber|MutableNumber|NumberInterface|ImmutableNumber[]|MutableNumber[]|NumberInterface[] |
||
| 88 | */ |
||
| 89 | public static function makeOrDont($type, $value, $precision = null, $base = 10) |
||
| 117 | |||
| 118 | public static function makeFractionFromString($value, $type = self::IMMUTABLE_FRACTION) |
||
| 145 | |||
| 146 | /** |
||
| 147 | * @param int|null $precision |
||
| 148 | * |
||
| 149 | * @return NumberInterface |
||
| 150 | */ |
||
| 151 | View Code Duplication | public static function makePi($precision = null) |
|
| 167 | |||
| 168 | /** |
||
| 169 | * @param int|null $precision |
||
| 170 | * |
||
| 171 | * @return NumberInterface |
||
| 172 | */ |
||
| 173 | View Code Duplication | public static function makeTau($precision = null) |
|
| 187 | |||
| 188 | /** |
||
| 189 | * @param int|null $precision |
||
| 190 | * |
||
| 191 | * @return NumberInterface |
||
| 192 | */ |
||
| 193 | public static function make2Pi($precision = null) |
||
| 197 | |||
| 198 | /** |
||
| 199 | * @param int|null $precision |
||
| 200 | * |
||
| 201 | * @return NumberInterface |
||
| 202 | */ |
||
| 203 | View Code Duplication | public static function makeE($precision = null) |
|
| 219 | |||
| 220 | /** |
||
| 221 | * @param int|null $precision |
||
| 222 | * |
||
| 223 | * @return NumberInterface |
||
| 224 | */ |
||
| 225 | View Code Duplication | public static function makeGoldenRatio($precision = null) |
|
| 241 | |||
| 242 | /** |
||
| 243 | * @return ImmutableNumber |
||
| 244 | */ |
||
| 245 | public static function makeOne() |
||
| 249 | |||
| 250 | /** |
||
| 251 | * @return ImmutableNumber |
||
| 252 | */ |
||
| 253 | public static function makeZero() |
||
| 257 | |||
| 258 | } |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the interface: