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 Meta 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 Meta, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | class Meta |
||
22 | { |
||
23 | |||
24 | protected $dataPoints = array(); |
||
25 | protected $currentDataPoint; |
||
26 | protected $currentDataSet = array(); |
||
27 | protected $dataPointMeta; |
||
28 | protected $options = array( |
||
29 | 'default_bg_color' => 'e9e9e9', |
||
30 | 'default_line_thickness' => 'thick', |
||
31 | 'positive_color' => 'd8ffb7', |
||
32 | 'negative_color' => 'ffc56c', |
||
33 | ); |
||
34 | private $columnCount = 0; |
||
35 | |||
36 | /** |
||
37 | * Define an option and its value. |
||
38 | * |
||
39 | * @param string $key |
||
40 | * @param mixed $value |
||
41 | * @return $this For method chaining |
||
42 | * @throws \InvalidArgumentException if key type is not a string or integer |
||
43 | */ |
||
44 | View Code Duplication | public function setOption($key, $value) |
|
54 | |||
55 | /** |
||
56 | * Set a group of options at the same time |
||
57 | * |
||
58 | * @param array $options |
||
59 | * @return $this For method chaining |
||
60 | * @throws \InvalidArgumentException If input is not an array |
||
61 | */ |
||
62 | View Code Duplication | public function setOptions($options) |
|
72 | |||
73 | /** |
||
74 | * Get the value of a single option or return false if it doesn't exist |
||
75 | * |
||
76 | * @param string|integer $key |
||
77 | * @return string|integer|boolean The option value |
||
78 | */ |
||
79 | public function getOption($key) |
||
85 | |||
86 | /** |
||
87 | * Retrieve the full option array |
||
88 | * |
||
89 | * @return array |
||
90 | */ |
||
91 | public function getOptions() |
||
95 | |||
96 | /** |
||
97 | * Reset data set values for all data points back to the defaults as in the |
||
98 | * column definition |
||
99 | * |
||
100 | * @return $this For method chaining |
||
101 | */ |
||
102 | public function clear() |
||
112 | |||
113 | /** |
||
114 | * Remove reference to all existing data points |
||
115 | * |
||
116 | * @return $this For method chaining |
||
117 | */ |
||
118 | public function clearDataPoints() |
||
124 | |||
125 | /** |
||
126 | * Increment the value for a key that makes up part of the currently focused |
||
127 | * data object. |
||
128 | * |
||
129 | * @param string $key The index to use |
||
130 | * @param int $value The value to increment by |
||
131 | * @return $this |
||
132 | * @throws \Exception if there is no data point, column or invalid type |
||
133 | */ |
||
134 | public function increment($key, $value = 1) |
||
156 | |||
157 | /** |
||
158 | * Directly set the value for a key that makes up part of the currently focused |
||
159 | * data object. |
||
160 | * |
||
161 | * @param string $key The index to use |
||
162 | * @param string|integer $value The value to set |
||
163 | * @return $this |
||
164 | * @throws \Exception |
||
165 | */ |
||
166 | public function set($key, $value) |
||
179 | |||
180 | /** |
||
181 | * Set a focus for the current data object for which data can be manipulated. |
||
182 | * If the key doesn't exist, it will be created using default column values. |
||
183 | * |
||
184 | * @param string $key The reference key to be used |
||
185 | * @return $this |
||
186 | */ |
||
187 | public function setPoint($key) |
||
196 | |||
197 | /** |
||
198 | * Add a data point which will be shown on the report. This function should be |
||
199 | * used prior to any kind of data manipulation as it will not setup a data |
||
200 | * structure for manipulation. |
||
201 | * |
||
202 | * @param string $key The index to use |
||
203 | * @return $this |
||
204 | */ |
||
205 | public function addPoint($key) |
||
214 | |||
215 | /** |
||
216 | * Define a type of data that will be presented on the report or metadata that |
||
217 | * will be used for calculations. In the latter case, within the options array |
||
218 | * set "visible" to false. |
||
219 | * At a minimum, the key needs to be set. In this case, the type will be string |
||
220 | * and the key converted in to a title |
||
221 | * . |
||
222 | * |
||
223 | * @param string $key A reference key for the piece of data |
||
224 | * @param string|integer $defaultValue The default also defines type of column |
||
225 | * @param array $options Additional parameters for the column to use |
||
226 | * |
||
227 | * @return $this |
||
228 | */ |
||
229 | public function column( |
||
259 | |||
260 | /** |
||
261 | * Manually set the data which will be used for the current set. when |
||
262 | * using this function, it will expect an associative array of arrays where |
||
263 | * each entry has a further array of values which have a key matching that of |
||
264 | * the meta data. |
||
265 | * TODO - This is quite messy, rewrite more efficiently. |
||
266 | * |
||
267 | * @param array $inputData A set of data which conforms to the column specification |
||
268 | * @return $this |
||
269 | */ |
||
270 | public function setData($inputData) |
||
283 | |||
284 | /** |
||
285 | * Return the information array relating to a specific column |
||
286 | * |
||
287 | * @param string|integer $key |
||
288 | * @return array |
||
289 | * @throws \Exception |
||
290 | */ |
||
291 | public function columnInfo($key) |
||
299 | |||
300 | /** |
||
301 | * Retrieve the complete meta information relating to a data point |
||
302 | * |
||
303 | * @return array |
||
304 | * @throws \Exception if the current data set is pointer-less |
||
305 | */ |
||
306 | public function getPoint() |
||
314 | |||
315 | /** |
||
316 | * Return a specific piece of data from the current data point |
||
317 | * |
||
318 | * @param string|integer $key |
||
319 | * @return mixed |
||
320 | */ |
||
321 | public function getPointValue($key) |
||
327 | |||
328 | /** |
||
329 | * Return the number of data columns that have been defined |
||
330 | * |
||
331 | * @return integer |
||
332 | */ |
||
333 | public function columnCount() |
||
337 | |||
338 | /** |
||
339 | * Return the number of data points that have been defined |
||
340 | * |
||
341 | * @return integer |
||
342 | */ |
||
343 | public function dataCount() |
||
347 | |||
348 | /** |
||
349 | * Return all the column information |
||
350 | * |
||
351 | * @return array |
||
352 | */ |
||
353 | public function getIndex() |
||
357 | |||
358 | /** |
||
359 | * Return all the data from the current set |
||
360 | * |
||
361 | * @return array |
||
362 | */ |
||
363 | public function getDataSet() |
||
367 | |||
368 | /** |
||
369 | * Return the key for the current data point in focus |
||
370 | * |
||
371 | * @return string |
||
372 | */ |
||
373 | public function getPointKey() |
||
377 | } |
||
378 |
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.