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 |
||
53 | class KeyDescriptor |
||
|
|||
54 | { |
||
55 | /** |
||
56 | * Holds collection of named key values |
||
57 | * For e.g. the keypredicate Order_Details(OrderID=10248,ProductID=11) will |
||
58 | * stored in this array as: |
||
59 | * Array([OrderID] => Array( [0] => 10248 [1] => Object(Int32)), |
||
60 | * [ProductID] => Array( [0] => 11 [1] => Object(Int32))) |
||
61 | * Note: This is mutually exclusive with $_positionalValues. These values |
||
62 | * are not validated against entity's ResourceType, validation will happen |
||
63 | * once validate function is called, $_validatedNamedValues will hold |
||
64 | * validated values. |
||
65 | * |
||
66 | * @var array |
||
67 | */ |
||
68 | private $namedValues = []; |
||
69 | |||
70 | /** |
||
71 | * Holds collection of positional key values |
||
72 | * For e.g. the keypredicate Order_Details(10248, 11) will |
||
73 | * stored in this array as: |
||
74 | * Array([0] => Array( [0] => 10248 [1] => Object(Int32)), |
||
75 | * [1] => Array( [0] => 11 [1] => Object(Int32))) |
||
76 | * Note: This is mutually exclusive with $_namedValues. These values are not |
||
77 | * validated against entity's ResourceType, validation will happen once validate |
||
78 | * function is called, $_validatedNamedValues will hold validated values. |
||
79 | * |
||
80 | * @var array |
||
81 | */ |
||
82 | private $positionalValues = []; |
||
83 | |||
84 | /** |
||
85 | * Holds collection of positional or named values as named values. The validate |
||
86 | * function populates this collection. |
||
87 | * |
||
88 | * @var array |
||
89 | */ |
||
90 | private $validatedNamedValues = []; |
||
91 | |||
92 | /** |
||
93 | * Creates new instance of KeyDescriptor |
||
94 | * Note: The arguments $namedValues and $positionalValues are mutually |
||
95 | * exclusive. Either both or one will be empty array. |
||
96 | * |
||
97 | * @param array $namedValues Collection of named key values |
||
98 | * @param array $positionalValues Collection of positional key values |
||
99 | */ |
||
100 | private function __construct(array $namedValues, array $positionalValues) |
||
106 | |||
107 | /** |
||
108 | * @param string $keyString |
||
109 | * @param bool $isKey |
||
110 | * @param KeyDescriptor|null $keyDescriptor |
||
111 | * @return bool |
||
112 | */ |
||
113 | protected static function parseAndVerifyRawKeyPredicate($keyString, $isKey, KeyDescriptor &$keyDescriptor = null) |
||
125 | |||
126 | /** |
||
127 | * Gets collection of named key values. |
||
128 | * |
||
129 | * @return array[] |
||
130 | */ |
||
131 | public function getNamedValues() |
||
135 | |||
136 | /** |
||
137 | * Gets collection of positional key values. |
||
138 | * |
||
139 | * @return array[] |
||
140 | */ |
||
141 | public function getPositionalValues() |
||
145 | |||
146 | /** |
||
147 | * Gets collection of positional key values by reference. |
||
148 | * |
||
149 | * @return array[] |
||
150 | */ |
||
151 | public function &getPositionalValuesByRef() |
||
155 | |||
156 | /** |
||
157 | * Gets validated named key values, this array will be populated |
||
158 | * in validate function. |
||
159 | * |
||
160 | * @throws InvalidOperationException If this function invoked before invoking validate function |
||
161 | * |
||
162 | * @return array[] |
||
163 | */ |
||
164 | public function getValidatedNamedValues() |
||
174 | |||
175 | /** |
||
176 | * Checks whether the key values have name. |
||
177 | * |
||
178 | * @return bool |
||
179 | */ |
||
180 | public function areNamedValues() |
||
184 | |||
185 | /** |
||
186 | * Check whether this KeyDesciption has any key values. |
||
187 | * |
||
188 | * @return bool |
||
189 | */ |
||
190 | public function isEmpty() |
||
195 | |||
196 | /** |
||
197 | * Gets number of values in the key. |
||
198 | * |
||
199 | * @return int |
||
200 | */ |
||
201 | public function valueCount() |
||
211 | |||
212 | /** |
||
213 | * Attempts to parse value(s) of resource key(s) from the given key predicate |
||
214 | * and creates instance of KeyDescription representing the same, Once parsing |
||
215 | * is done one should call validate function to validate the created |
||
216 | * KeyDescription. |
||
217 | * |
||
218 | * @param string $keyPredicate The predicate to parse |
||
219 | * @param KeyDescriptor|null $keyDescriptor On return, Description of key after parsing |
||
220 | * |
||
221 | * @return bool True if the given values were parsed; false if there was a syntax error |
||
222 | */ |
||
223 | public static function tryParseKeysFromKeyPredicate( |
||
231 | |||
232 | /** |
||
233 | * Attempt to parse comma separated values representing a skiptoken and creates |
||
234 | * instance of KeyDescriptor representing the same. |
||
235 | * |
||
236 | * @param string $skipToken The skiptoken value to parse |
||
237 | * @param KeyDescriptor &$keyDescriptor On return, Description of values |
||
238 | * after parsing |
||
239 | * |
||
240 | * @return bool True if the given values were parsed; false if there was a syntax error |
||
241 | */ |
||
242 | public static function tryParseValuesFromSkipToken($skipToken, &$keyDescriptor) |
||
248 | |||
249 | /** |
||
250 | * Validate this KeyDescriptor, If valid, this function populates |
||
251 | * _validatedNamedValues array with key as keyName and value as an array of |
||
252 | * key value and key type. |
||
253 | * |
||
254 | * @param string $segmentAsString The segment in the form identifier |
||
255 | * (keyPredicate) which this descriptor |
||
256 | * represents |
||
257 | * @param ResourceType $resourceType The type of the identifier in the segment |
||
258 | * |
||
259 | * @throws ODataException If validation fails |
||
260 | */ |
||
261 | public function validate($segmentAsString, ResourceType $resourceType) |
||
349 | |||
350 | /** |
||
351 | * Attempts to parse value(s) of resource key(s) from the key predicate and |
||
352 | * creates instance of KeyDescription representing the same, Once parsing is |
||
353 | * done, one should call validate function to validate the created KeyDescription. |
||
354 | * |
||
355 | * @param string $keyPredicate The key predicate to parse |
||
356 | * @param bool $allowNamedValues Set to true if parser should accept |
||
357 | * named values(Property = KeyValue), |
||
358 | * if false then parser will fail on |
||
359 | * such constructs |
||
360 | * @param bool $allowNull Set to true if parser should accept |
||
361 | * null values for positional key |
||
362 | * values, if false then parser will |
||
363 | * fail on seeing null values |
||
364 | * @param KeyDescriptor &$keyDescriptor On return, Description of key after |
||
365 | * parsing |
||
366 | * |
||
367 | * @return bool True if the given values were parsed; false if there was a syntax error |
||
368 | */ |
||
369 | private static function tryParseKeysFromRawKeyPredicate( |
||
470 | |||
471 | /** |
||
472 | * Get the type of an Astoria URI key value, validate the value against the type. If valid, this function |
||
473 | * provides the PHP value equivalent to the Astoria URI key value. |
||
474 | * |
||
475 | * @param string $value The Astoria URI key value |
||
476 | * @param ExpressionTokenId $tokenId The tokenId for $value literal |
||
477 | * @param mixed|null &$outValue After the invocation, this parameter holds the PHP equivalent to $value, |
||
478 | * if $value is not valid then this parameter will be null |
||
479 | * @param IType|null &$outType After the invocation, this parameter holds the type of $value, if $value is |
||
480 | * not a valid key value type then this parameter will be null |
||
481 | * |
||
482 | * @return bool True if $value is a valid type, else false |
||
483 | */ |
||
484 | private static function getTypeAndValidateKeyValue($value, $tokenId, &$outValue, &$outType) |
||
531 | |||
532 | /** |
||
533 | * Generate relative edit url for this key descriptor and supplied resource set |
||
534 | * |
||
535 | * @param ResourceSet $resourceSet |
||
536 | * |
||
537 | * @return string |
||
538 | * @throws \InvalidArgumentException |
||
539 | */ |
||
540 | public function generateRelativeUri(ResourceSet $resourceSet) |
||
571 | } |
||
572 |