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: