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 DataSet 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 DataSet, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
11 | class DataSet implements \ArrayAccess, \Iterator |
||
12 | { |
||
13 | /** |
||
14 | * @var array Data wrapped by DataSet. |
||
15 | * */ |
||
16 | private $data = []; |
||
17 | |||
18 | /** |
||
19 | * @var array Pointer of current insert/read position in internal format. |
||
20 | * |
||
21 | * The path is represented as an array of strings representing a route |
||
22 | * through the levels of the DataSet to the required value. |
||
23 | */ |
||
24 | private $currentPath = []; |
||
25 | |||
26 | /** |
||
27 | * @param array $data Data to be wrapped. |
||
28 | */ |
||
29 | 30 | public function __construct(array $data = []) |
|
33 | |||
34 | /** |
||
35 | * Get wrapped data. |
||
36 | * |
||
37 | * @return array Currently wrapped data. |
||
38 | */ |
||
39 | 7 | public function getData() |
|
43 | |||
44 | /** |
||
45 | * Assign new data to DataSet. |
||
46 | * |
||
47 | * @param array $data Data to be wrapped by DataSet. |
||
48 | */ |
||
49 | 30 | public function setData(array $data) |
|
53 | |||
54 | /** |
||
55 | * Move into a level. |
||
56 | * |
||
57 | * @param string|int $level The level to move into. |
||
58 | */ |
||
59 | 11 | public function push($level) |
|
63 | |||
64 | /** |
||
65 | * Move back out of the current level. |
||
66 | */ |
||
67 | 1 | public function pop() |
|
71 | |||
72 | /** |
||
73 | * Set a value in the current level. |
||
74 | * |
||
75 | * @param string|int $name The name of the value to add. |
||
76 | * @param string|int|array|null $value The value to add. |
||
77 | */ |
||
78 | 12 | public function setValue($name, $value) |
|
93 | |||
94 | /** |
||
95 | * Get a value by name from the current level. |
||
96 | * |
||
97 | * @param string|int $name The name of the value to retrieve. |
||
98 | * @return string|int|array|null The found value. Returns null if the value cannot be found. |
||
99 | */ |
||
100 | 2 | public function getValue($name) |
|
114 | |||
115 | /** |
||
116 | * Find a value by path within the DataSet instance. |
||
117 | * |
||
118 | * @see $currentPath |
||
119 | * @param string|array $path Path in internal or human format. |
||
120 | * @return string|int|array|null The found value. Returns null if the value cannot be found. |
||
121 | */ |
||
122 | 11 | public function getValueByPath($path) |
|
140 | |||
141 | /** |
||
142 | * Assign a value by path within the DataSet instance, |
||
143 | * overwrites any existing value. |
||
144 | * |
||
145 | * @see $currentPath |
||
146 | * @param string|array $path A path in internal or human format. |
||
147 | * @param string|int|array|null $value The value to assign. |
||
148 | */ |
||
149 | 2 | public function setValueByPath($path, $value) |
|
169 | |||
170 | /** |
||
171 | * Transform human path to internal DataSet format. |
||
172 | * |
||
173 | * @see $currentPath |
||
174 | * @param string $path Path in human format ('/a/b' or 'a/../b' or './b/c'). |
||
175 | * @return array Path in internal format. |
||
176 | * @throws Field\ConfigurationException If path could not be parsed. |
||
177 | */ |
||
178 | 16 | public function parsePath($path) |
|
202 | |||
203 | /** |
||
204 | * Determines whether a given string is a DataSet path. |
||
205 | * |
||
206 | * @param mixed $value Tested string. |
||
207 | * @return bool Whether tested string is a DataSet path. |
||
208 | * @since 0.2 |
||
209 | */ |
||
210 | 44 | public static function isPath($value) |
|
214 | |||
215 | /** |
||
216 | * Recursively find value by path. |
||
217 | * |
||
218 | * @param string $value Value path. |
||
219 | * @return array|int|null|string |
||
220 | * @since 1.0 |
||
221 | */ |
||
222 | 5 | public function resolvePath($value) |
|
230 | |||
231 | /** |
||
232 | * Return current internal read|write position. |
||
233 | * |
||
234 | * @return array |
||
235 | */ |
||
236 | 3 | public function getCurrentPath() |
|
240 | |||
241 | /** |
||
242 | * Return value, stored in DataSet by current internal read|write position. |
||
243 | * |
||
244 | * @return array|int|null|string |
||
245 | */ |
||
246 | 3 | public function getValueByCurrentPath() |
|
250 | |||
251 | /** |
||
252 | * @inheritdoc |
||
253 | * */ |
||
254 | 1 | public function current() |
|
258 | |||
259 | /** |
||
260 | * @inheritdoc |
||
261 | * */ |
||
262 | 3 | public function next() |
|
266 | |||
267 | /** |
||
268 | * @inheritdoc |
||
269 | * */ |
||
270 | 3 | public function key() |
|
274 | |||
275 | /** |
||
276 | * @inheritdoc |
||
277 | * */ |
||
278 | 3 | public function valid() |
|
282 | |||
283 | /** |
||
284 | * @inheritdoc |
||
285 | * */ |
||
286 | 3 | public function rewind() |
|
290 | |||
291 | /** |
||
292 | * @inheritdoc |
||
293 | * */ |
||
294 | 2 | public function offsetExists($offset) |
|
298 | |||
299 | /** |
||
300 | * @inheritdoc |
||
301 | * */ |
||
302 | 3 | public function offsetGet($offset) |
|
306 | |||
307 | /** |
||
308 | * @inheritdoc |
||
309 | * */ |
||
310 | 1 | public function offsetSet($offset, $value) |
|
314 | |||
315 | /** |
||
316 | * @inheritdoc |
||
317 | * */ |
||
318 | 1 | public function offsetUnset($offset) |
|
322 | } |
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.