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 AbstractApi 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 AbstractApi, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | abstract class AbstractApi implements ApiInterface |
||
19 | { |
||
20 | /** |
||
21 | * API sub namespace |
||
22 | * |
||
23 | * @var string |
||
24 | */ |
||
25 | protected $path; |
||
26 | |||
27 | /** |
||
28 | * The client |
||
29 | * |
||
30 | * @var Client |
||
31 | */ |
||
32 | protected $client; |
||
33 | |||
34 | /** |
||
35 | * @var array |
||
36 | */ |
||
37 | public static $fields; |
||
38 | |||
39 | /** |
||
40 | * @param Client $client |
||
41 | */ |
||
42 | 334 | public function __construct(Client $client) |
|
46 | |||
47 | /** |
||
48 | * Catches any undefined "get{$field}" calls, and passes them |
||
49 | * to the getField() if the $field is in the $this->fields property |
||
50 | * |
||
51 | * @param string $method called method |
||
52 | * @param array $arguments array of arguments passed to called method |
||
53 | * |
||
54 | * @return array |
||
55 | * |
||
56 | * @throws BadMethodCallException If the method does not start with "get" |
||
57 | * or the field is not included in the $fields property |
||
58 | */ |
||
59 | public function __call($method, $arguments) |
||
74 | |||
75 | /** |
||
76 | * Get field names (properties) |
||
77 | * |
||
78 | * @return array array of fields |
||
79 | */ |
||
80 | public function getFields() |
||
84 | |||
85 | /** |
||
86 | * Get a field value by field name |
||
87 | * |
||
88 | * @param string $id the board's id |
||
89 | * @param string $field the field |
||
90 | * |
||
91 | * @return mixed field value |
||
92 | * |
||
93 | * @throws InvalidArgumentException If the field does not exist |
||
94 | */ |
||
95 | 5 | public function getField($id, $field) |
|
105 | |||
106 | /** |
||
107 | * Send a GET request with query parameters. |
||
108 | * |
||
109 | * @param string $path Request path. |
||
110 | * @param array $parameters GET parameters. |
||
111 | * @param array $requestHeaders Request Headers. |
||
112 | * |
||
113 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string |
||
114 | */ |
||
115 | protected function get($path, array $parameters = array(), $requestHeaders = array()) |
||
121 | |||
122 | /** |
||
123 | * Send a HEAD request with query parameters |
||
124 | * |
||
125 | * @param string $path Request path. |
||
126 | * @param array $parameters HEAD parameters. |
||
127 | * @param array $requestHeaders Request headers. |
||
128 | * |
||
129 | * @return \Guzzle\Http\Message\Response |
||
130 | */ |
||
131 | protected function head($path, array $parameters = array(), $requestHeaders = array()) |
||
139 | |||
140 | /** |
||
141 | * Send a POST request with JSON-encoded parameters. |
||
142 | * |
||
143 | * @param string $path Request path. |
||
144 | * @param array $parameters POST parameters to be JSON encoded. |
||
145 | * @param array $requestHeaders Request headers. |
||
146 | * |
||
147 | * @return mixed |
||
148 | */ |
||
149 | protected function post($path, array $parameters = array(), $requestHeaders = array()) |
||
157 | |||
158 | /** |
||
159 | * Send a POST request with raw data. |
||
160 | * |
||
161 | * @param string $path Request path. |
||
162 | * @param mixed $body Request body. |
||
163 | * @param array $requestHeaders Request headers. |
||
164 | * |
||
165 | * @return \Guzzle\Http\EntityBodyInterface|mixed|string |
||
166 | */ |
||
167 | protected function postRaw($path, $body, $requestHeaders = array()) |
||
177 | |||
178 | /** |
||
179 | * Send a PATCH request with JSON-encoded parameters. |
||
180 | * |
||
181 | * @param string $path Request path. |
||
182 | * @param array $parameters POST parameters to be JSON encoded. |
||
183 | * @param array $requestHeaders Request headers. |
||
184 | * |
||
185 | * @return mixed |
||
186 | */ |
||
187 | View Code Duplication | protected function patch($path, array $parameters = array(), $requestHeaders = array()) |
|
197 | |||
198 | /** |
||
199 | * Send a PUT request with JSON-encoded parameters. |
||
200 | * |
||
201 | * @param string $path Request path. |
||
202 | * @param array $parameters POST parameters to be JSON encoded. |
||
203 | * @param array $requestHeaders Request headers. |
||
204 | * |
||
205 | * @return mixed |
||
206 | */ |
||
207 | protected function put($path, array $parameters = array(), $requestHeaders = array()) |
||
223 | |||
224 | /** |
||
225 | * Send a DELETE request with JSON-encoded parameters. |
||
226 | * |
||
227 | * @param string $path Request path. |
||
228 | * @param array $parameters POST parameters to be JSON encoded. |
||
229 | * @param array $requestHeaders Request headers. |
||
230 | * |
||
231 | * @return mixed |
||
232 | */ |
||
233 | View Code Duplication | protected function delete($path, array $parameters = array(), $requestHeaders = array()) |
|
243 | |||
244 | /** |
||
245 | * Prepare request parameters. |
||
246 | * |
||
247 | * @param array $parameters Request parameters |
||
248 | * |
||
249 | * @return null|string |
||
250 | */ |
||
251 | protected function createParametersBody(array $parameters) |
||
271 | |||
272 | 191 | protected function getPath($id = null) |
|
280 | |||
281 | /** |
||
282 | * Validate parameters array |
||
283 | * |
||
284 | * @param string[] $required required properties (array keys) |
||
285 | * @param array $params array to check for existence of the required keys |
||
286 | * |
||
287 | * @throws MissingArgumentException if a required parameter is missing |
||
288 | */ |
||
289 | 30 | View Code Duplication | protected function validateRequiredParameters(array $required, array $params) |
297 | |||
298 | /** |
||
299 | * Validate allowed parameters array |
||
300 | * Checks whether the passed parameters are allowed |
||
301 | * |
||
302 | * @param string[] $allowed allowed properties |
||
303 | * @param array|string $params array to check |
||
304 | * @param string $paramName |
||
305 | * |
||
306 | * @return array array of validated parameters |
||
307 | * |
||
308 | * @throws InvalidArgumentException if a parameter is not allowed |
||
309 | */ |
||
310 | 70 | protected function validateAllowedParameters(array $allowed, $params, $paramName) |
|
329 | |||
330 | /** |
||
331 | * Validate that the params array includes at least one of |
||
332 | * the keys in a given array |
||
333 | * |
||
334 | * @param string[] $atLeastOneOf allowed properties |
||
335 | * @param array $params array to check |
||
336 | * |
||
337 | * @return boolean |
||
338 | * |
||
339 | * @throws MissingArgumentException |
||
340 | */ |
||
341 | 6 | View Code Duplication | protected function validateAtLeastOneOf(array $atLeastOneOf, array $params) |
354 | } |
||
355 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.