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 KeyDescriptor 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 KeyDescriptor, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
52 | class KeyDescriptor |
||
|
|||
53 | { |
||
54 | /** |
||
55 | * Holds collection of named key values |
||
56 | * For e.g. the keypredicate Order_Details(OrderID=10248,ProductID=11) will |
||
57 | * stored in this array as: |
||
58 | * Array([OrderID] => Array( [0] => 10248 [1] => Object(Int32)), |
||
59 | * [ProductID] => Array( [0] => 11 [1] => Object(Int32))) |
||
60 | * Note: This is mutually exclusive with $_positionalValues. These values |
||
61 | * are not validated aganist entity's ResourceType, validation will happen |
||
62 | * once validate function is called, $_validatedNamedValues will hold |
||
63 | * validated values. |
||
64 | * |
||
65 | * @var array |
||
66 | */ |
||
67 | private $namedValues = []; |
||
68 | |||
69 | /** |
||
70 | * Holds collection of positional key values |
||
71 | * For e.g. the keypredicate Order_Details(10248, 11) will |
||
72 | * stored in this array as: |
||
73 | * Array([0] => Array( [0] => 10248 [1] => Object(Int32)), |
||
74 | * [1] => Array( [0] => 11 [1] => Object(Int32))) |
||
75 | * Note: This is mutually exclusive with $_namedValues. These values are not |
||
76 | * validated aganist entity's ResourceType, validation will happen once validate |
||
77 | * function is called, $_validatedNamedValues will hold validated values. |
||
78 | * |
||
79 | * @var array |
||
80 | */ |
||
81 | private $positionalValues = []; |
||
82 | |||
83 | /** |
||
84 | * Holds collection of positional or named values as named values. The validate |
||
85 | * function populates this collection. |
||
86 | * |
||
87 | * @var array |
||
88 | */ |
||
89 | private $validatedNamedValues = []; |
||
90 | |||
91 | /** |
||
92 | * Creates new instance of KeyDescriptor |
||
93 | * Note: The arguments $namedValues and $positionalValues are mutually |
||
94 | * exclusive. Either both or one will be empty array. |
||
95 | * |
||
96 | * @param array $namedValues Collection of named key values |
||
97 | * @param array $positionalValues Collection of positional key values |
||
98 | */ |
||
99 | private function __construct(array $namedValues, array $positionalValues) |
||
105 | |||
106 | /** |
||
107 | * Gets collection of named key values. |
||
108 | * |
||
109 | * @return array[] |
||
110 | */ |
||
111 | public function getNamedValues() |
||
115 | |||
116 | /** |
||
117 | * Gets collection of positional key values. |
||
118 | * |
||
119 | * @return array[] |
||
120 | */ |
||
121 | public function getPositionalValues() |
||
125 | |||
126 | /** |
||
127 | * Gets collection of positional key values by reference. |
||
128 | * |
||
129 | * @return array[] |
||
130 | */ |
||
131 | public function &getPositionalValuesByRef() |
||
135 | |||
136 | /** |
||
137 | * Gets validated named key values, this array will be populated |
||
138 | * in validate function. |
||
139 | * |
||
140 | * @throws InvalidOperationException If this function invoked before invoking validate function |
||
141 | * |
||
142 | * @return array[] |
||
143 | */ |
||
144 | public function getValidatedNamedValues() |
||
154 | |||
155 | /** |
||
156 | * Checks whether the key values have name. |
||
157 | * |
||
158 | * @return bool |
||
159 | */ |
||
160 | public function areNamedValues() |
||
164 | |||
165 | /** |
||
166 | * Check whether this KeyDesciption has any key values. |
||
167 | * |
||
168 | * @return bool |
||
169 | */ |
||
170 | public function isEmpty() |
||
175 | |||
176 | /** |
||
177 | * Gets number of values in the key. |
||
178 | * |
||
179 | * @return int |
||
180 | */ |
||
181 | public function valueCount() |
||
191 | |||
192 | /** |
||
193 | * Attempts to parse value(s) of resource key(s) from the given key predicate |
||
194 | * and creates instance of KeyDescription representing the same, Once parsing |
||
195 | * is done one should call validate function to validate the created |
||
196 | * KeyDescription. |
||
197 | * |
||
198 | * @param string $keyPredicate The predicate to parse |
||
199 | * @param KeyDescriptor KeyDescriptor On return, Description of key after |
||
200 | * parsing |
||
201 | * |
||
202 | * @return bool True if the given values were parsed; false if there was a syntax error |
||
203 | */ |
||
204 | public static function tryParseKeysFromKeyPredicate( |
||
215 | |||
216 | /** |
||
217 | * Attempt to parse comma seperated values representing a skiptoken and creates |
||
218 | * instance of KeyDescriptor representing the same. |
||
219 | * |
||
220 | * @param string $skipToken The skiptoken value to parse |
||
221 | * @param KeyDescriptor &$keyDescriptor On return, Description of values |
||
222 | * after parsing |
||
223 | * |
||
224 | * @return bool True if the given values were parsed; false if there was a syntax error |
||
225 | */ |
||
226 | public static function tryParseValuesFromSkipToken($skipToken, &$keyDescriptor) |
||
235 | |||
236 | /** |
||
237 | * Validate this KeyDescriptor, If valid, this function populates |
||
238 | * _validatedNamedValues array with key as keyName and value as an array of |
||
239 | * key value and key type. |
||
240 | * |
||
241 | * @param string $segmentAsString The segment in the form identifier |
||
242 | * (keyPredicate) which this descriptor |
||
243 | * represents |
||
244 | * @param ResourceType $resourceType The type of the identifier in the segment |
||
245 | * |
||
246 | * @throws ODataException If validation fails |
||
247 | */ |
||
248 | public function validate($segmentAsString, ResourceType $resourceType) |
||
334 | |||
335 | /** |
||
336 | * Attempts to parse value(s) of resource key(s) from the key predicate and |
||
337 | * creates instance of KeyDescription representing the same, Once parsing is |
||
338 | * done, one should call validate function to validate the created KeyDescription. |
||
339 | * |
||
340 | * @param string $keyPredicate The key predicate to parse |
||
341 | * @param bool $allowNamedValues Set to true if parser should accept |
||
342 | * named values(Property = KeyValue), |
||
343 | * if false then parser will fail on |
||
344 | * such constructs |
||
345 | * @param bool $allowNull Set to true if parser should accept |
||
346 | * null values for positional key |
||
347 | * values, if false then parser will |
||
348 | * fail on seeing null values |
||
349 | * @param KeyDescriptor &$keyDescriptor On return, Description of key after |
||
350 | * parsing |
||
351 | * |
||
352 | * @return bool True if the given values were parsed; false if there was a syntax error |
||
353 | */ |
||
354 | private static function tryParseKeysFromRawKeyPredicate( |
||
456 | |||
457 | /** |
||
458 | * Get the type of an Astoria URI key value, validate the value against the type. If valid, this function |
||
459 | * provides the PHP value equivalent to the Astoria URI key value. |
||
460 | * |
||
461 | * @param string $value The Astoria URI key value |
||
462 | * @param ExpressionTokenId $tokenId The tokenId for $value literal |
||
463 | * @param mixed|null &$outValue After the invocation, this parameter holds the PHP equivalent to $value, |
||
464 | * if $value is not valid then this parameter will be null |
||
465 | * @param IType|null &$outType After the invocation, this parameter holds the type of $value, if $value is |
||
466 | * not a valid key value type then this parameter will be null |
||
467 | * |
||
468 | * @return bool True if $value is a valid type, else false |
||
469 | */ |
||
470 | private static function getTypeAndValidateKeyValue($value, $tokenId, &$outValue, &$outType) |
||
517 | } |
||
518 |