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 RequestExpander 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 RequestExpander, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | class RequestExpander |
||
17 | { |
||
18 | /** |
||
19 | * Description of the OData request that a client has submitted. |
||
20 | * |
||
21 | * @var RequestDescription |
||
22 | */ |
||
23 | private $request; |
||
24 | |||
25 | /** |
||
26 | * Holds reference to the data service instance. |
||
27 | * |
||
28 | * @var IService |
||
29 | */ |
||
30 | private $service; |
||
31 | |||
32 | /** |
||
33 | * Holds reference to the wrapper over IDSMP and IDSQP implementation. |
||
34 | * |
||
35 | * @var ProvidersWrapper |
||
36 | */ |
||
37 | private $providers; |
||
38 | |||
39 | /** |
||
40 | * Holds reference to segment stack being processed. |
||
41 | * |
||
42 | * @var SegmentStack |
||
43 | */ |
||
44 | private $stack; |
||
45 | |||
46 | public function __construct(RequestDescription $request, IService $service, ProvidersWrapper $wrapper) |
||
53 | |||
54 | /** |
||
55 | * Gets reference to the request submitted by client. |
||
56 | * |
||
57 | * @return RequestDescription |
||
58 | */ |
||
59 | public function getRequest() |
||
63 | |||
64 | /** |
||
65 | * Gets reference to the request submitted by client. |
||
66 | * |
||
67 | * @return ProvidersWrapper |
||
68 | */ |
||
69 | public function getProviders() |
||
73 | |||
74 | /** |
||
75 | * Gets the data service instance. |
||
76 | * |
||
77 | * @return IService |
||
78 | */ |
||
79 | public function getService() |
||
83 | |||
84 | /** |
||
85 | * Gets the segment stack instance. |
||
86 | * |
||
87 | * @return SegmentStack |
||
88 | */ |
||
89 | public function getStack() |
||
93 | |||
94 | /** |
||
95 | * Perform expansion. |
||
96 | * |
||
97 | * @return void |
||
98 | */ |
||
99 | public function handleExpansion() |
||
111 | |||
112 | /** |
||
113 | * Execute queries for expansion. |
||
114 | * |
||
115 | * @param array|mixed $result Resource(s) whose navigation properties needs to be expanded |
||
116 | */ |
||
117 | private function executeExpansion($result) |
||
161 | |||
162 | /** |
||
163 | * Resource set wrapper for the resource being retrieved. |
||
164 | * |
||
165 | * @return ResourceSetWrapper |
||
166 | */ |
||
167 | View Code Duplication | private function getCurrentResourceSetWrapper() |
|
174 | |||
175 | /** |
||
176 | * Pushes a segment for the root of the tree |
||
177 | * Note: Calls to this method should be balanced with calls to popSegment. |
||
178 | * |
||
179 | * @return bool true if the segment was pushed, false otherwise |
||
180 | */ |
||
181 | private function pushSegmentForRoot() |
||
188 | |||
189 | /** |
||
190 | * Pushes a segment for the current navigation property being written out. |
||
191 | * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about |
||
192 | * 'Segment Stack' and this method. |
||
193 | * Note: Calls to this method should be balanced with calls to popSegment. |
||
194 | * |
||
195 | * @param ResourceProperty &$resourceProperty Current navigation property |
||
196 | * being written out |
||
197 | * |
||
198 | * @throws InvalidOperationException If this function invoked with non-navigation |
||
199 | * property instance |
||
200 | * |
||
201 | * @return bool true if a segment was pushed, false otherwise |
||
202 | */ |
||
203 | private function pushSegmentForNavigationProperty(ResourceProperty &$resourceProperty) |
||
229 | |||
230 | /** |
||
231 | * Gets collection of expanded projection nodes under the current node. |
||
232 | * |
||
233 | * @return ExpandedProjectionNode[] List of nodes describing expansions for the current segment |
||
234 | */ |
||
235 | protected function getExpandedProjectionNodes() |
||
249 | |||
250 | /** |
||
251 | * Find a 'ExpandedProjectionNode' instance in the projection tree |
||
252 | * which describes the current segment. |
||
253 | * |
||
254 | * @return ExpandedProjectionNode|null |
||
255 | */ |
||
256 | View Code Duplication | private function getCurrentExpandedProjectionNode() |
|
276 | |||
277 | /** |
||
278 | * Pushes information about the segment whose instance is going to be |
||
279 | * retrieved from the IDSQP implementation |
||
280 | * Note: Calls to this method should be balanced with calls to popSegment. |
||
281 | * |
||
282 | * @param string $segmentName Name of segment to push |
||
283 | * @param ResourceSetWrapper &$resourceSetWrapper The resource set wrapper |
||
284 | * to push |
||
285 | * |
||
286 | * @return bool true if the segment was push, false otherwise |
||
287 | */ |
||
288 | private function pushSegment($segmentName, ResourceSetWrapper &$resourceSetWrapper) |
||
292 | |||
293 | /** |
||
294 | * Pops segment information from the 'Segment Stack' |
||
295 | * Note: Calls to this method should be balanced with previous calls |
||
296 | * to _pushSegment. |
||
297 | * |
||
298 | * @param bool $needPop Is a pop required. Only true if last push |
||
299 | * was successful |
||
300 | * |
||
301 | * @throws InvalidOperationException If found un-balanced call |
||
302 | * with _pushSegment |
||
303 | */ |
||
304 | private function popSegment($needPop) |
||
308 | |||
309 | /** |
||
310 | * @param ExpandedProjectionNode $expandedProjectionNode |
||
311 | * @param $entry |
||
312 | * |
||
313 | * @return object[]|null |
||
314 | */ |
||
315 | private function executeCollectionExpansionGetRelated($expandedProjectionNode, $entry) |
||
336 | |||
337 | /** |
||
338 | * @param ExpandedProjectionNode $expandedProjectionNode |
||
339 | * @param $entry |
||
340 | * @param \POData\Providers\Metadata\ResourceType $resourceType |
||
341 | * @param string $expandedPropertyName |
||
342 | * |
||
343 | * @throws InvalidOperationException |
||
344 | * @throws \POData\Common\ODataException |
||
345 | */ |
||
346 | private function executeSingleExpansionGetRelated( |
||
368 | |||
369 | /** |
||
370 | * @param $entry |
||
371 | * @param $result |
||
372 | * @param ExpandedProjectionNode $expandedProjectionNode |
||
373 | * @param \POData\Providers\Metadata\ResourceType $resourceType |
||
374 | * @param string $expandedPropertyName |
||
375 | * |
||
376 | * @throws InvalidOperationException |
||
377 | */ |
||
378 | private function executeCollectionExpansionProcessExpansion( |
||
399 | |||
400 | /** |
||
401 | * @param $result |
||
402 | * @param ExpandedProjectionNode $expandedProjectionNode |
||
403 | * |
||
404 | * @throws InvalidOperationException |
||
405 | */ |
||
406 | private function pushPropertyToNavigation($result, $expandedProjectionNode) |
||
413 | } |
||
414 |