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 RequestBuilder 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 RequestBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
9 | class RequestBuilder implements RequestBuilderInterface |
||
10 | { |
||
11 | private $method; |
||
12 | |||
13 | private $headers; |
||
14 | |||
15 | private $queries; |
||
16 | |||
17 | private $body; |
||
18 | |||
19 | private $options; |
||
20 | |||
21 | private $postBody; |
||
22 | |||
23 | private $cookies; |
||
24 | |||
25 | private $securityExtensions; |
||
26 | |||
27 | private $credentials; |
||
28 | |||
29 | private $uri; |
||
30 | |||
31 | private $requestBuilders; |
||
32 | |||
33 | private $files; |
||
34 | |||
35 | private static function getAcceptedMethods() |
||
36 | { |
||
37 | return [ |
||
38 | RequestInterface::GET, |
||
39 | RequestInterface::PUT, |
||
40 | RequestInterface::POST, |
||
41 | RequestInterface::DELETE, |
||
42 | RequestInterface::HEAD, |
||
43 | RequestInterface::OPTIONS, |
||
44 | RequestInterface::PATCH |
||
45 | ]; |
||
46 | } |
||
47 | |||
48 | public function __construct() |
||
49 | { |
||
50 | $this->requestBuilders = []; |
||
51 | $this->options = []; |
||
52 | $this->securityExtensions = []; |
||
53 | $this->credentials = []; |
||
54 | $this->files = []; |
||
55 | } |
||
56 | |||
57 | public function build($uri = null, array $queries = null, array $headers = null, array $postBody = null, $body = null, array $options = []) |
||
|
|||
58 | { |
||
59 | $this->setUri($uri ?: $this->uri); |
||
60 | $this->setQueries($queries ?: $this->queries); |
||
61 | $this->setHeaders($headers ?: $this->headers); |
||
62 | $this->setPostBody($postBody ?: $this->postBody); |
||
63 | $this->setBody($body ?: $this->body); |
||
64 | $this->setOptions($options ?: $this->options); |
||
65 | |||
66 | if (null === $this->method) { |
||
67 | throw new \RuntimeException('You can\'t build a request without any methods'); |
||
68 | } |
||
69 | |||
70 | if (!isset($this->requestBuilders[$this->method])) { |
||
71 | throw new \RuntimeException(sprintf( |
||
72 | 'No RequestBuilder exists for method "%s"', |
||
73 | $this->method |
||
74 | )); |
||
75 | } |
||
76 | |||
77 | $client = $this->requestBuilders[$this->method]->getClient(); |
||
78 | |||
79 | foreach ($this->securityExtensions as $extension) { |
||
80 | $extension->secureClient($client, $this); |
||
81 | } |
||
82 | |||
83 | $request = $this->requestBuilders[$this->method]->build( |
||
84 | $this->getUri(), |
||
85 | $this->getQueries(), |
||
86 | $this->getHeaders(), |
||
87 | $this->getPostBody(), |
||
88 | $this->getBody(), |
||
89 | $this->getOptions() |
||
90 | ); |
||
91 | |||
92 | if (null !== $this->cookies) { |
||
93 | foreach ($this->cookies as $name => $cookie) { |
||
94 | $request->addCookie($name, $cookie); |
||
95 | } |
||
96 | } |
||
97 | |||
98 | foreach ($this->securityExtensions as $extension) { |
||
99 | $extension->secureRequest($request, $this); |
||
100 | } |
||
101 | |||
102 | foreach ($this->files as $file) { |
||
103 | $request->addPostFile($file['name'], $file['path']); |
||
104 | } |
||
105 | |||
106 | $this->clean(); |
||
107 | |||
108 | return $request; |
||
109 | } |
||
110 | |||
111 | public function setMethod($method) |
||
127 | |||
128 | public function addRequestBuilder(RequestBuilderInterface $builder, $method) |
||
147 | |||
148 | public function getMethod() |
||
152 | |||
153 | public function setHeaders(array $headers = null) |
||
159 | |||
160 | public function getHeaders() |
||
164 | |||
165 | public function setBody($body = null) |
||
171 | |||
172 | public function getQueries() |
||
176 | |||
177 | public function setQueries(array $queries = null) |
||
183 | |||
184 | public function getBody() |
||
188 | |||
189 | public function setOptions(array $options = null) |
||
195 | |||
196 | public function getOptions() |
||
200 | |||
201 | public function setPostBody(array $postBody = null) |
||
202 | { |
||
203 | $this->postBody = $postBody; |
||
204 | |||
205 | return $this; |
||
206 | } |
||
207 | |||
208 | public function getPostBody() |
||
209 | { |
||
210 | return $this->postBody; |
||
211 | } |
||
212 | |||
213 | public function getCookies() |
||
214 | { |
||
215 | return $this->cookies; |
||
216 | } |
||
217 | |||
218 | public function setCookies(array $cookies = null) |
||
219 | { |
||
220 | $this->cookies = $cookies; |
||
221 | |||
222 | return $this; |
||
223 | } |
||
224 | |||
225 | public function addSecurityExtension(SecurityExtensionInterface $extension) |
||
226 | { |
||
227 | $this->securityExtensions[] = $extension; |
||
228 | |||
229 | return $this; |
||
230 | } |
||
231 | |||
232 | public function getSecurityExtensions() |
||
233 | { |
||
234 | return $this->securityExtensions; |
||
235 | } |
||
236 | |||
237 | public function getCredentials() |
||
238 | { |
||
239 | return $this->credentials; |
||
240 | } |
||
241 | |||
242 | public function setCredentials(array $credentials) |
||
243 | { |
||
244 | $this->credentials = $credentials; |
||
245 | |||
246 | return $this; |
||
247 | } |
||
248 | |||
249 | public function setUri($uri = null) |
||
255 | |||
256 | public function getUri() |
||
260 | |||
261 | public function addFile($name, $path) |
||
262 | { |
||
263 | $this->files[] = ['name' => $name, 'path' => $path]; |
||
264 | |||
265 | return $this; |
||
266 | } |
||
267 | |||
268 | public function setClient(ClientInterface $client = null) |
||
271 | |||
272 | public function getClient() |
||
273 | { |
||
274 | } |
||
275 | |||
276 | protected function clean() |
||
277 | { |
||
278 | $this->uri = null; |
||
279 | $this->method = null; |
||
280 | $this->queries = null; |
||
281 | $this->body = null; |
||
282 | $this->postBody = null; |
||
283 | $this->cookies = null; |
||
284 | $this->headers = null; |
||
290 | } |
||
291 |
Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a
@return
annotation as described here.