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 UndoUpvoteAnswer 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 UndoUpvoteAnswer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | final class UndoUpvoteAnswer |
||
35 | { |
||
36 | const URL = 'answers/'; |
||
37 | const QUERY_PARAMS = [ |
||
38 | 'order' => 'desc', |
||
39 | 'sort' => 'activity', |
||
40 | 'site' => 'stackoverflow', |
||
41 | 'filter' => HttpClient::FILTER_ALL, |
||
42 | ]; |
||
43 | |||
44 | /** |
||
45 | * The authentication. |
||
46 | * |
||
47 | * @var Authentication|null |
||
48 | */ |
||
49 | private $authentication; |
||
50 | |||
51 | /** |
||
52 | * Constructor. |
||
53 | * |
||
54 | * @param Authentication|null $anAuthentication The authentication |
||
55 | */ |
||
56 | public function __construct(Authentication $anAuthentication = null) |
||
60 | |||
61 | /** |
||
62 | * Get all answers on the site. |
||
63 | * |
||
64 | * More info: http://api.stackexchange.com/docs/answers |
||
65 | * |
||
66 | * @param array $params QueryString parameter(s) |
||
67 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
68 | * |
||
69 | * @return array |
||
70 | */ |
||
71 | View Code Duplication | public function all(array $params = ['site' => 'stackoverflow'], $serialize = true) |
|
85 | |||
86 | /** |
||
87 | * Get answers identified by a set of ids. |
||
88 | * |
||
89 | * More info: http://api.stackexchange.com/docs/answers-by-ids |
||
90 | * |
||
91 | * @param string|array $ids Array which contains the ids delimited by semicolon, or a simple id |
||
92 | * @param array $params QueryString parameter(s) |
||
93 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
94 | * |
||
95 | * @return array|Answer |
||
96 | */ |
||
97 | public function getOfIds($ids, array $params = ['site' => 'stackoverflow'], $serialize = true) |
||
111 | |||
112 | /** |
||
113 | * Casts an accept vote on the given answer. |
||
114 | * |
||
115 | * More info: https://api.stackexchange.com/docs/accept-answer |
||
116 | * |
||
117 | * @param string $id The id of question |
||
118 | * @param array $params QueryString parameter(s) |
||
119 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
120 | * |
||
121 | * @throws \Exception when the auth is null |
||
122 | * |
||
123 | * @return Answer |
||
124 | */ |
||
125 | public function accept($id, array $params = self::QUERY_PARAMS, $serialize = true) |
||
126 | { |
||
127 | if (!$this->authentication instanceof Authentication) { |
||
128 | throw new \Exception('Authentication is required'); |
||
129 | } |
||
130 | $response = HttpClient::instance()->put( |
||
131 | self::URL . $id . '/accept', array_merge($params, $this->authentication->toArray()) |
||
132 | ); |
||
133 | |||
134 | return $serialize === true ? AnswerSerializer::serialize($response) : $response; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * Undoes an accept vote on the given answer. |
||
139 | * |
||
140 | * More info: https://api.stackexchange.com/docs/undo-accept-answer |
||
141 | * |
||
142 | * @param string $id The id of question |
||
143 | * @param array $params QueryString parameter(s) |
||
144 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
145 | * |
||
146 | * @throws \Exception when the auth is null |
||
147 | * |
||
148 | * @return Answer |
||
149 | */ |
||
150 | public function undoAccept($id, array $params = self::QUERY_PARAMS, $serialize = true) |
||
151 | { |
||
152 | if (!$this->authentication instanceof Authentication) { |
||
153 | throw new \Exception('Authentication is required'); |
||
154 | } |
||
155 | $response = HttpClient::instance()->put( |
||
156 | self::URL . $id . '/accept/undo', array_merge($params, $this->authentication->toArray()) |
||
157 | ); |
||
158 | |||
159 | return $serialize === true ? AnswerSerializer::serialize($response) : $response; |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * Deletes an answer. |
||
164 | * |
||
165 | * More info: https://api.stackexchange.com/docs/delete-answer |
||
166 | * |
||
167 | * @param string $id The id of question |
||
168 | * @param array $params QueryString parameter(s) |
||
169 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
170 | * |
||
171 | * @throws \Exception when the auth is null |
||
172 | * |
||
173 | * @return Answer |
||
174 | */ |
||
175 | public function delete($id, array $params = self::QUERY_PARAMS, $serialize = true) |
||
176 | { |
||
177 | if (!$this->authentication instanceof Authentication) { |
||
178 | throw new \Exception('Authentication is required'); |
||
179 | } |
||
180 | $response = HttpClient::instance()->delete( |
||
181 | self::URL . $id . '/delete', array_merge($params, $this->authentication->toArray()) |
||
182 | ); |
||
183 | |||
184 | return $serialize === true ? AnswerSerializer::serialize($response) : $response; |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * Downvotes an answer. |
||
189 | * |
||
190 | * More info: https://api.stackexchange.com/docs/downvote-answer |
||
191 | * |
||
192 | * @param string $id The id of question |
||
193 | * @param array $params QueryString parameter(s) |
||
194 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
195 | * |
||
196 | * @throws \Exception when the auth is null |
||
197 | * |
||
198 | * @return Answer |
||
199 | */ |
||
200 | public function downvote($id, array $params = self::QUERY_PARAMS, $serialize = true) |
||
201 | { |
||
202 | if (!$this->authentication instanceof Authentication) { |
||
203 | throw new \Exception('Authentication is required'); |
||
204 | } |
||
205 | $response = HttpClient::instance()->put( |
||
206 | self::URL . $id . '/downvote', array_merge($params, $this->authentication->toArray()) |
||
207 | ); |
||
208 | |||
209 | return $serialize === true ? AnswerSerializer::serialize($response) : $response; |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Undoes an downvote on an answer. |
||
214 | * |
||
215 | * More info: https://api.stackexchange.com/docs/undo-downvote-answer |
||
216 | * |
||
217 | * @param string $id The id of question |
||
218 | * @param array $params QueryString parameter(s) |
||
219 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
220 | * |
||
221 | * @throws \Exception when the auth is null |
||
222 | * |
||
223 | * @return Answer |
||
224 | */ |
||
225 | public function undoDownvote($id, array $params = self::QUERY_PARAMS, $serialize = true) |
||
226 | { |
||
227 | if (!$this->authentication instanceof Authentication) { |
||
228 | throw new \Exception('Authentication is required'); |
||
229 | } |
||
230 | $response = HttpClient::instance()->put( |
||
231 | self::URL . $id . '/downvote/undo', array_merge($params, $this->authentication->toArray()) |
||
232 | ); |
||
233 | |||
234 | return $serialize === true ? AnswerSerializer::serialize($response) : $response; |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Edit an existing answer. |
||
239 | * |
||
240 | * More info: https://api.stackexchange.com/docs/edit-answer |
||
241 | * |
||
242 | * @param string $id The id of question |
||
243 | * @param string $body The body of the answer |
||
244 | * @param array $params QueryString parameter(s) |
||
245 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
246 | * |
||
247 | * @throws \Exception when the auth is null |
||
248 | * |
||
249 | * @return Answer |
||
250 | */ |
||
251 | View Code Duplication | public function update($id, $body, array $params = self::QUERY_PARAMS, $serialize = true) |
|
262 | |||
263 | /** |
||
264 | * Upvotes an answer. |
||
265 | * |
||
266 | * More info: https://api.stackexchange.com/docs/upvote-answer |
||
267 | * |
||
268 | * @param string $id The id of question |
||
269 | * @param array $params QueryString parameter(s) |
||
270 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
271 | * |
||
272 | * @throws \Exception when the auth is null |
||
273 | * |
||
274 | * @return Answer |
||
275 | */ |
||
276 | public function upvote($id, array $params = self::QUERY_PARAMS, $serialize = true) |
||
277 | { |
||
278 | if (!$this->authentication instanceof Authentication) { |
||
279 | throw new \Exception('Authentication is required'); |
||
280 | } |
||
281 | $response = HttpClient::instance()->put( |
||
282 | self::URL . $id . '/upvote', array_merge($params, $this->authentication->toArray()) |
||
283 | ); |
||
284 | |||
285 | return $serialize === true ? AnswerSerializer::serialize($response) : $response; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Undoes an upvote on an answer. |
||
290 | * |
||
291 | * More info: https://api.stackexchange.com/docs/undo-upvote-answer |
||
292 | * |
||
293 | * @param string $id The id of question |
||
294 | * @param array $params QueryString parameter(s) |
||
295 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
296 | * |
||
297 | * @throws \Exception when the auth is null |
||
298 | * |
||
299 | * @return Answer |
||
300 | */ |
||
301 | public function undoUpvote($id, array $params = self::QUERY_PARAMS, $serialize = true) |
||
302 | { |
||
303 | if (!$this->authentication instanceof Authentication) { |
||
304 | throw new \Exception('Authentication is required'); |
||
305 | } |
||
306 | $response = HttpClient::instance()->put( |
||
307 | self::URL . $id . '/upvote/undo', array_merge($params, $this->authentication->toArray()) |
||
308 | ); |
||
309 | |||
310 | return $serialize === true ? AnswerSerializer::serialize($response) : $response; |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | * Casts a flag against the answer identified by id. |
||
315 | * |
||
316 | * More info: https://api.stackexchange.com/docs/create-answer-flag |
||
317 | * |
||
318 | * @param string $id The id of question |
||
319 | * @param array $params QueryString parameter(s) |
||
320 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
321 | * |
||
322 | * @throws \Exception when the auth is null |
||
323 | * |
||
324 | * @return Answer |
||
325 | */ |
||
326 | public function addFlag($id, array $params = self::QUERY_PARAMS, $serialize = true) |
||
327 | { |
||
328 | if (!$this->authentication instanceof Authentication) { |
||
329 | throw new \Exception('Authentication is required'); |
||
330 | } |
||
331 | $response = HttpClient::instance()->put( |
||
332 | self::URL . $id . '/flags/add', array_merge($params, $this->authentication->toArray()) |
||
333 | ); |
||
334 | |||
335 | return $serialize === true ? AnswerSerializer::serialize($response) : $response; |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * Gets the answers to a set of questions identified in id. |
||
340 | * |
||
341 | * More info: https://api.stackexchange.com/docs/answers-on-questions |
||
342 | * |
||
343 | * @param string|array $ids Array which contains the ids delimited by semicolon, or a simple id |
||
344 | * @param array $params QueryString parameter(s) |
||
345 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
346 | * |
||
347 | * @return array|Answer |
||
348 | */ |
||
349 | public function getOfQuestionIds($ids, array $params = ['site' => 'stackoverflow'], $serialize = true) |
||
363 | |||
364 | /** |
||
365 | * Create a new answer on the given question. |
||
366 | * |
||
367 | * More info: https://api.stackexchange.com/docs/create-answer |
||
368 | * |
||
369 | * @param string $id The id of question |
||
370 | * @param string $body The body of the answer |
||
371 | * @param array $params QueryString parameter(s) |
||
372 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
373 | * |
||
374 | * @throws \Exception when the auth is null |
||
375 | * |
||
376 | * @return Answer |
||
377 | */ |
||
378 | View Code Duplication | public function addOfQuestionId($id, $body, array $params = self::QUERY_PARAMS, $serialize = true) |
|
391 | |||
392 | /** |
||
393 | * Render an answer given it's body and the question it's on. |
||
394 | * |
||
395 | * More info: https://api.stackexchange.com/docs/render-answer |
||
396 | * |
||
397 | * @param string $id The id of question |
||
398 | * @param string $body The body of the answer |
||
399 | * @param array $params QueryString parameter(s) |
||
400 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
401 | * |
||
402 | * @throws \Exception when the auth is null |
||
403 | * |
||
404 | * @return Answer |
||
405 | */ |
||
406 | public function render($id, $body, array $params = ['site' => 'stackoverflow'], $serialize = true) |
||
422 | |||
423 | /** |
||
424 | * Returns the answers the users in {ids} have posted. |
||
425 | * |
||
426 | * More info: https://api.stackexchange.com/docs/answers-on-users |
||
427 | * |
||
428 | * @param string|array $ids Array which contains the ids delimited by semicolon, or a simple id |
||
429 | * @param array $params QueryString parameter(s) |
||
430 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
431 | * |
||
432 | * @return array|Answer |
||
433 | */ |
||
434 | View Code Duplication | public function getOfUserIds($ids, array $params = ['site' => 'stackoverflow'], $serialize = true) |
|
448 | |||
449 | /** |
||
450 | * Returns the answers owned by the user associated with the given access_token. |
||
451 | * |
||
452 | * More info: https://api.stackexchange.com/docs/me-answers |
||
453 | * |
||
454 | * @param array $params QueryString parameter(s) |
||
455 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
456 | * |
||
457 | * @throws \Exception when the auth is null |
||
458 | * |
||
459 | * @return array|Answer |
||
460 | */ |
||
461 | public function myAnswers(array $params = self::QUERY_PARAMS, $serialize = true) |
||
462 | { |
||
463 | if (!$this->authentication instanceof Authentication) { |
||
464 | throw new \Exception('Authentication is required'); |
||
465 | } |
||
466 | $response = HttpClient::instance()->get( |
||
467 | 'me/' . self::URL, array_merge($params, $this->authentication->toArray()) |
||
468 | ); |
||
469 | |||
470 | return $serialize === true ? AnswerSerializer::serialize($response) : $response; |
||
471 | } |
||
472 | |||
473 | /** |
||
474 | * Returns the top 30 answers a user has posted in response to questions with the given tags. |
||
475 | * |
||
476 | * More info: https://api.stackexchange.com/docs/top-user-answers-in-tags |
||
477 | * |
||
478 | * @param string $userId The user id |
||
479 | * @param string|array $tags Array which contains the $tags delimited by semicolon, or a simple tag |
||
480 | * @param array $params QueryString parameter(s) |
||
481 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
482 | * |
||
483 | * @return array|Answer |
||
484 | */ |
||
485 | View Code Duplication | public function getTopOfUserAndTags($userId, $tags, array $params = ['site' => 'stackoverflow'], $serialize = true) |
|
499 | |||
500 | /** |
||
501 | * Returns the top 30 answers the user associated with the given |
||
502 | * access_token has posted in response to questions with the given tags. |
||
503 | * |
||
504 | * More info: https://api.stackexchange.com/docs/me-tags-top-answers |
||
505 | * |
||
506 | * @param string|array $tags Array which contains the tags delimited by semicolon, or a simple tag |
||
507 | * @param array $params QueryString parameter(s) |
||
508 | * @param bool $serialize Checks if the result will be serialize or not, by default is true |
||
509 | * |
||
510 | * @throws \Exception when the auth is null |
||
511 | * |
||
512 | * @return array|Answer |
||
513 | */ |
||
514 | public function myTopAnswersOfTags($tags, array $params = self::QUERY_PARAMS, $serialize = true) |
||
526 | } |
||
527 |
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.