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 Operator 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 Operator, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
14 | class Operator implements ArrayableInterface |
||
15 | { |
||
16 | /** |
||
17 | * |
||
18 | * @var array list of update operations |
||
19 | */ |
||
20 | private $operators = array(); |
||
21 | |||
22 | public function set($fieldName, $value) |
||
32 | |||
33 | public function push($fieldName, $value) |
||
61 | |||
62 | public function pushEach($fieldName, array $values) |
||
63 | { |
||
64 | // value must be list, not dictionary |
||
65 | $values = array_values($values); |
||
66 | |||
67 | // prepare to store |
||
68 | $values = Structure::prepareToStore($values); |
||
69 | |||
70 | // no $push operator found |
||
71 | if (!isset($this->operators['$push'])) { |
||
72 | $this->operators['$push'] = array(); |
||
73 | } |
||
74 | |||
75 | // no field name found |
||
76 | if (!isset($this->operators['$push'][$fieldName])) { |
||
77 | $this->operators['$push'][$fieldName] = array( |
||
78 | '$each' => $values |
||
79 | ); |
||
80 | } // field name found and has single value |
||
81 | elseif (!is_array($this->operators['$push'][$fieldName]) || !isset($this->operators['$push'][$fieldName]['$each'])) { |
||
82 | $oldValue = $this->operators['$push'][$fieldName]; |
||
83 | $this->operators['$push'][$fieldName] = array( |
||
84 | '$each' => array_merge(array($oldValue), $values) |
||
85 | ); |
||
86 | } // field name found and already $each |
||
87 | else { |
||
88 | $this->operators['$push'][$fieldName]['$each'] = array_merge( |
||
89 | $this->operators['$push'][$fieldName]['$each'], |
||
90 | $values |
||
91 | ); |
||
92 | } |
||
93 | |||
94 | return $this; |
||
95 | } |
||
96 | |||
97 | /** |
||
98 | * The $slice modifier limits the number of array elements during a |
||
99 | * $push operation. To project, or return, a specified number of array |
||
100 | * elements from a read operation, see the $slice projection operator instead. |
||
101 | * |
||
102 | * @link http://docs.mongodb.org/manual/reference/operator/update/slice |
||
103 | * @param string $field |
||
104 | * @param int $slice |
||
105 | * @return \Sokil\Mongo\Operator |
||
106 | * @throws \Sokil\Mongo\Exception |
||
107 | */ |
||
108 | public function pushEachSlice($field, $slice) |
||
120 | |||
121 | /** |
||
122 | * The $sort modifier orders the elements of an array during a $push operation. |
||
123 | * |
||
124 | * @link http://docs.mongodb.org/manual/reference/operator/update/sort |
||
125 | * @param string $field |
||
126 | * @param array $sort |
||
127 | * @return \Sokil\Mongo\Operator |
||
128 | * @throws \Sokil\Mongo\Exception |
||
129 | */ |
||
130 | View Code Duplication | public function pushEachSort($field, array $sort) |
|
145 | |||
146 | /** |
||
147 | * The $position modifier specifies the location in the array at which |
||
148 | * the $push operator insert elements. Without the $position modifier, |
||
149 | * the $push operator inserts elements to the end of the array. See |
||
150 | * $push modifiers for more information. |
||
151 | * |
||
152 | * @link http://docs.mongodb.org/manual/reference/operator/update/position |
||
153 | * @param string $field |
||
154 | * @param int $position non-negative number that corresponds to the position in the array, based on a zero-based index |
||
155 | * @return \Sokil\Mongo\Operator |
||
156 | * @throws \Sokil\Mongo\Exception |
||
157 | */ |
||
158 | View Code Duplication | public function pushEachPosition($field, $position) |
|
175 | |||
176 | public function addToSet($field, $value) |
||
200 | |||
201 | public function addToSetEach($field, array $values) |
||
230 | |||
231 | public function increment($fieldName, $value = 1) |
||
243 | |||
244 | /** |
||
245 | * The $pull operator removes from an existing array all instances of a |
||
246 | * value or values that match a specified query. |
||
247 | * |
||
248 | * @link http://docs.mongodb.org/manual/reference/operator/update/pull |
||
249 | * @param integer|string|\Sokil\Mongo\Expression|callable $expression |
||
250 | * @param mixed|\Sokil\Mongo\Expression|callable $value |
||
251 | * @return \Sokil\Mongo\Operator |
||
252 | */ |
||
253 | public function pull($expression, $value = null) |
||
296 | |||
297 | /** |
||
298 | * The $unset operator deletes a particular field |
||
299 | * |
||
300 | * @link http://docs.mongodb.org/manual/reference/operator/update/unset |
||
301 | * @param string $fieldName |
||
302 | * @return \Sokil\Mongo\Operator |
||
303 | */ |
||
304 | public function unsetField($fieldName) |
||
309 | |||
310 | public function bitwiceAnd($field, $value) |
||
315 | |||
316 | public function bitwiceOr($field, $value) |
||
321 | |||
322 | public function bitwiceXor($field, $value) |
||
328 | |||
329 | public function isDefined() |
||
333 | |||
334 | public function reset() |
||
339 | |||
340 | public function get($operation, $fieldName = null) |
||
352 | |||
353 | /** |
||
354 | * @deprecated since v.1.13 use Operator::toArray() |
||
355 | * @return array |
||
356 | */ |
||
357 | public function getAll() |
||
361 | |||
362 | public function toArray() |
||
366 | |||
367 | public function isReloadRequired() |
||
371 | |||
372 | /** |
||
373 | * Transform operator in different formats to canonical array form |
||
374 | * |
||
375 | * @param mixed $mixed |
||
376 | * @return array |
||
377 | * @throws \Sokil\Mongo\Exception |
||
378 | */ |
||
379 | public static function convertToArray($mixed) |
||
397 | } |
||
398 |
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.