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 |
||
15 | class RequestExpander |
||
16 | { |
||
17 | /** |
||
18 | * Description of the OData request that a client has submitted. |
||
19 | * |
||
20 | * @var RequestDescription |
||
21 | */ |
||
22 | private $request; |
||
23 | |||
24 | /** |
||
25 | * Holds reference to the data service instance. |
||
26 | * |
||
27 | * @var IService |
||
28 | */ |
||
29 | private $service; |
||
30 | |||
31 | /** |
||
32 | * Holds reference to the wrapper over IDSMP and IDSQP implementation. |
||
33 | * |
||
34 | * @var ProvidersWrapper |
||
35 | */ |
||
36 | private $providers; |
||
37 | |||
38 | /** |
||
39 | * Holds reference to segment stack being processed |
||
40 | * |
||
41 | * @var SegmentStack |
||
42 | */ |
||
43 | private $stack; |
||
44 | |||
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 | public function handleExpansion() |
||
98 | { |
||
99 | $node = $this->getRequest()->getRootProjectionNode(); |
||
100 | if (!is_null($node) && $node->isExpansionSpecified()) { |
||
101 | $result = $this->getRequest()->getTargetResult(); |
||
102 | if (!is_null($result) && (!is_array($result) || !empty($result))) { |
||
103 | $needPop = $this->pushSegmentForRoot(); |
||
104 | $this->executeExpansion($result); |
||
105 | $this->popSegment(true === $needPop); |
||
106 | } |
||
107 | } |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Execute queries for expansion. |
||
112 | * |
||
113 | * @param array|mixed $result Resource(s) whose navigation properties needs to be expanded |
||
114 | */ |
||
115 | private function executeExpansion($result) |
||
156 | |||
157 | /** |
||
158 | * Resource set wrapper for the resource being retrieved. |
||
159 | * |
||
160 | * @return ResourceSetWrapper |
||
161 | */ |
||
162 | private function getCurrentResourceSetWrapper() |
||
168 | |||
169 | /** |
||
170 | * Pushes a segment for the root of the tree |
||
171 | * Note: Calls to this method should be balanced with calls to popSegment. |
||
172 | * |
||
173 | * @return bool true if the segment was pushed, false otherwise |
||
174 | */ |
||
175 | private function pushSegmentForRoot() |
||
182 | |||
183 | /** |
||
184 | * Pushes a segment for the current navigation property being written out. |
||
185 | * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about |
||
186 | * 'Segment Stack' and this method. |
||
187 | * Note: Calls to this method should be balanced with calls to popSegment. |
||
188 | * |
||
189 | * @param ResourceProperty &$resourceProperty Current navigation property |
||
190 | * being written out |
||
191 | * |
||
192 | * @return bool true if a segment was pushed, false otherwise |
||
193 | * |
||
194 | * @throws InvalidOperationException If this function invoked with non-navigation |
||
195 | * property instance |
||
196 | */ |
||
197 | private function pushSegmentForNavigationProperty(ResourceProperty &$resourceProperty) |
||
223 | |||
224 | /** |
||
225 | * Gets collection of expanded projection nodes under the current node. |
||
226 | * |
||
227 | * @return ExpandedProjectionNode[] List of nodes describing expansions for the current segment |
||
228 | */ |
||
229 | protected function getExpandedProjectionNodes() |
||
230 | { |
||
231 | $expandedProjectionNode = $this->getCurrentExpandedProjectionNode(); |
||
232 | $expandedProjectionNodes = array(); |
||
233 | if (!is_null($expandedProjectionNode)) { |
||
234 | foreach ($expandedProjectionNode->getChildNodes() as $node) { |
||
235 | if ($node instanceof ExpandedProjectionNode) { |
||
236 | $expandedProjectionNodes[] = $node; |
||
237 | } |
||
238 | } |
||
239 | } |
||
240 | |||
241 | return $expandedProjectionNodes; |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * Find a 'ExpandedProjectionNode' instance in the projection tree |
||
246 | * which describes the current segment. |
||
247 | * |
||
248 | * @return ExpandedProjectionNode|null |
||
249 | */ |
||
250 | private function getCurrentExpandedProjectionNode() |
||
270 | |||
271 | /** |
||
272 | * Pushes information about the segment whose instance is going to be |
||
273 | * retrieved from the IDSQP implementation |
||
274 | * Note: Calls to this method should be balanced with calls to popSegment. |
||
275 | * |
||
276 | * @param string $segmentName Name of segment to push |
||
277 | * @param ResourceSetWrapper &$resourceSetWrapper The resource set wrapper |
||
278 | * to push |
||
279 | * |
||
280 | * @return bool true if the segment was push, false otherwise |
||
281 | */ |
||
282 | private function pushSegment($segmentName, ResourceSetWrapper &$resourceSetWrapper) |
||
286 | |||
287 | /** |
||
288 | * Pops segment information from the 'Segment Stack' |
||
289 | * Note: Calls to this method should be balanced with previous calls |
||
290 | * to _pushSegment. |
||
291 | * |
||
292 | * @param bool $needPop Is a pop required. Only true if last push |
||
293 | * was successful |
||
294 | * |
||
295 | * @throws InvalidOperationException If found un-balanced call |
||
296 | * with _pushSegment |
||
297 | */ |
||
298 | private function popSegment($needPop) |
||
302 | |||
303 | /** |
||
304 | * @param $expandedProjectionNode |
||
305 | * @param $entry |
||
306 | * @return null|\object[] |
||
307 | */ |
||
308 | private function executeCollectionExpansionGetRelated($expandedProjectionNode, $entry) |
||
328 | |||
329 | /** |
||
330 | * @param $expandedProjectionNode |
||
331 | * @param $entry |
||
332 | * @param $resourceType |
||
333 | * @param $expandedPropertyName |
||
334 | * @throws InvalidOperationException |
||
335 | * @throws \POData\Common\ODataException |
||
336 | */ |
||
337 | private function executeSingleExpansionGetRelated( |
||
359 | |||
360 | /** |
||
361 | * @param $entry |
||
362 | * @param $result |
||
363 | * @param $expandedProjectionNode |
||
364 | * @param $resourceType |
||
365 | * @param $expandedPropertyName |
||
366 | * @throws InvalidOperationException |
||
367 | */ |
||
368 | private function executeCollectionExpansionProcessExpansion( |
||
390 | |||
391 | /** |
||
392 | * @param $result |
||
393 | * @param $expandedProjectionNode |
||
394 | * @throws InvalidOperationException |
||
395 | */ |
||
396 | private function pushPropertyToNavigation($result, $expandedProjectionNode) |
||
403 | } |
||
404 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: