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 Lexer 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 Lexer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
33 | class Lexer |
||
34 | { |
||
35 | /** |
||
36 | * Static cache of the compiled regular expression. |
||
37 | * |
||
38 | * @var string |
||
39 | */ |
||
40 | protected $compiledRegex; |
||
41 | |||
42 | /** |
||
43 | * The list of registered operators. |
||
44 | * |
||
45 | * @var array |
||
46 | */ |
||
47 | protected $operators = []; |
||
48 | |||
49 | /** |
||
50 | * The list of registered constants. |
||
51 | * |
||
52 | * @var array |
||
53 | */ |
||
54 | protected $constants = []; |
||
55 | |||
56 | /** |
||
57 | * The list of registered functions. |
||
58 | * |
||
59 | * @var array |
||
60 | */ |
||
61 | protected $functions = []; |
||
62 | |||
63 | /** |
||
64 | * The list of parsed variables. |
||
65 | * |
||
66 | * @var array |
||
67 | */ |
||
68 | protected $variables = []; |
||
69 | |||
70 | /** |
||
71 | * Registers a function with the lexer. |
||
72 | * |
||
73 | * @param string $name |
||
74 | * The name of the function. |
||
75 | * @param callable $function |
||
76 | * The callback to be executed. |
||
77 | * @param int $arguments |
||
78 | * The number of arguments required for the function. |
||
79 | * |
||
80 | * @return $this |
||
81 | */ |
||
82 | public function addFunction($name, callable $function, $arguments = 1) |
||
87 | |||
88 | /** |
||
89 | * Removes a function from the function registry. |
||
90 | * |
||
91 | * @param string $name |
||
92 | * The name of the function to remove. |
||
93 | * |
||
94 | * @return $this |
||
95 | */ |
||
96 | public function removeFunction($name) |
||
101 | |||
102 | /** |
||
103 | * Registers an operator with the lexer. |
||
104 | * |
||
105 | * @param string $name |
||
106 | * The name of the operator. |
||
107 | * @param string $operator |
||
108 | * The full qualified class name of the operator token. |
||
109 | * |
||
110 | * @return $this |
||
111 | */ |
||
112 | public function addOperator($name, $operator) |
||
124 | |||
125 | /** |
||
126 | * Removes an operator from the operator registry. |
||
127 | * |
||
128 | * @param string $name |
||
129 | * The name of the operator to remove. |
||
130 | * |
||
131 | * @return $this |
||
132 | */ |
||
133 | public function removeOperator($name) |
||
142 | |||
143 | /** |
||
144 | * Registers a constant with the lexer. |
||
145 | * |
||
146 | * @param string $name |
||
147 | * The name of the constant. |
||
148 | * @param int $value |
||
149 | * The value of the constant. |
||
150 | * |
||
151 | * @return $this |
||
152 | */ |
||
153 | public function addConstant($name, $value) |
||
158 | |||
159 | /** |
||
160 | * Removes a constant from the constant registry. |
||
161 | * |
||
162 | * @param string $name |
||
163 | * The name of the constant to remove. |
||
164 | * |
||
165 | * @return $this |
||
166 | */ |
||
167 | public function removeConstant($name) |
||
172 | |||
173 | /** |
||
174 | * Generates a token stream from a mathematical expression. |
||
175 | * |
||
176 | * @param string $input |
||
177 | * The mathematical expression to tokenize. |
||
178 | * |
||
179 | * @return array |
||
180 | * The generated token stream. |
||
181 | */ |
||
182 | public function tokenize($input) |
||
208 | |||
209 | /** |
||
210 | * Reorganizes a list of tokens into reverse polish (postfix) notation. |
||
211 | * |
||
212 | * Uses an implementation of the Shunting-yard algorithm. |
||
213 | * |
||
214 | * http://en.wikipedia.org/wiki/Shunting-yard_algorithm |
||
215 | * |
||
216 | * @param \Fubhy\Math\Token\TokenInterface[] $tokens |
||
217 | * The tokens to be reorganized into reverse polish (postfix) notation. |
||
218 | * |
||
219 | * @return \Fubhy\Math\Token\TokenInterface[] |
||
220 | * The given tokens in reverse polish (postfix) notation. |
||
221 | * |
||
222 | * @throws \Fubhy\Math\Exception\IncorrectParenthesisException |
||
223 | * @throws \Fubhy\Math\Exception\IncorrectExpressionException |
||
224 | */ |
||
225 | public function postfix($tokens) |
||
293 | |||
294 | /** |
||
295 | * Creates a token object of the given type. |
||
296 | * |
||
297 | * @param string $type |
||
298 | * The type of the token. |
||
299 | * @param string $value |
||
300 | * The matched string. |
||
301 | * @param int $offset |
||
302 | * The offset of the matched string. |
||
303 | * @param $match |
||
304 | * The full match as returned by preg_match_all(). |
||
305 | * |
||
306 | * @return \Fubhy\Math\Token\TokenInterface |
||
307 | * The created token object. |
||
308 | * |
||
309 | * @throws \Fubhy\Math\Exception\UnknownConstantException |
||
310 | * @throws \Fubhy\Math\Exception\UnknownFunctionException |
||
311 | * @throws \Fubhy\Math\Exception\UnknownOperatorException |
||
312 | * @throws \Fubhy\Math\Exception\UnknownTokenException |
||
313 | */ |
||
314 | protected function createToken($type, $value, $offset, $match) |
||
360 | |||
361 | /** |
||
362 | * Builds a concatenated regular expression for all available operators. |
||
363 | * |
||
364 | * @return string |
||
365 | * The regular expression for matching all available operators. |
||
366 | */ |
||
367 | protected function getOperatorRegex() |
||
376 | |||
377 | /** |
||
378 | * Compiles the regular expressions of all token types. |
||
379 | * |
||
380 | * @return string |
||
381 | * The compiled regular expression. |
||
382 | */ |
||
383 | protected function getCompiledTokenRegex() |
||
403 | |||
404 | /** |
||
405 | * Return the parsed variables identifiers. |
||
406 | * |
||
407 | * @return array |
||
408 | */ |
||
409 | public function getVariables() |
||
413 | |||
414 | } |
||
415 |
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.