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 Api 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 Api, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | class Api |
||
32 | { |
||
33 | /** |
||
34 | * The authentication client. |
||
35 | * |
||
36 | * @var Authentication |
||
37 | */ |
||
38 | private $auth; |
||
39 | |||
40 | /** |
||
41 | * The constructor. |
||
42 | * |
||
43 | * @param Authentication $auth The authentication client. |
||
44 | */ |
||
45 | 54 | public function __construct(Authentication $auth) |
|
49 | |||
50 | /** |
||
51 | * Processes a response. |
||
52 | * |
||
53 | * @param Response $response The response object. |
||
54 | * @param callable $processor The response processor. |
||
55 | * |
||
56 | * @return Response The response |
||
57 | */ |
||
58 | 11 | private function processResponse(Response $response, $processor) |
|
67 | |||
68 | /** |
||
69 | * Execute the given http request. |
||
70 | * |
||
71 | * @param Request $request |
||
72 | * @param callable|null $processor |
||
73 | * |
||
74 | * @throws RateLimitedReached |
||
75 | * |
||
76 | * @return Response The response |
||
77 | */ |
||
78 | 34 | public function execute(Request $request, $processor = null) |
|
79 | 1 | { |
|
80 | 34 | $response = $this->auth->execute($request); |
|
81 | |||
82 | 34 | if ($response->rateLimited()) { |
|
83 | 19 | throw new RateLimitedReached($response); |
|
84 | } |
||
85 | |||
86 | 15 | if (is_callable($processor)) { |
|
87 | 11 | $response = $this->processResponse($response, $processor); |
|
88 | 6 | } |
|
89 | |||
90 | 15 | return $response; |
|
91 | } |
||
92 | |||
93 | /** |
||
94 | * Fetch a single user and processes the response. |
||
95 | * |
||
96 | * @param Request $request |
||
97 | * |
||
98 | * @throws RateLimitedReached |
||
99 | * |
||
100 | * @return Response The response |
||
101 | */ |
||
102 | 4 | View Code Duplication | private function fetchUser(Request $request) |
112 | |||
113 | /** |
||
114 | * Fetch a single board and processes the response. |
||
115 | * |
||
116 | * @param Request $request |
||
117 | * |
||
118 | * @throws RateLimitedReached |
||
119 | * |
||
120 | * @return Response The response |
||
121 | */ |
||
122 | 4 | View Code Duplication | private function fetchBoard(Request $request) |
132 | |||
133 | /** |
||
134 | * Fetch a single pin and processes the response. |
||
135 | * |
||
136 | * @param Request $request |
||
137 | * |
||
138 | * @throws RateLimitedReached |
||
139 | * |
||
140 | * @return Response The response |
||
141 | */ |
||
142 | 8 | View Code Duplication | private function fetchPin(Request $request) |
143 | { |
||
144 | 8 | $request->setFields(Pin::fields()); |
|
145 | |||
146 | return $this->execute($request, function (Response $response) { |
||
147 | $mapper = new Mapper(new Pin()); |
||
148 | |||
149 | return $mapper->toSingle($response); |
||
150 | 8 | }); |
|
151 | } |
||
152 | |||
153 | /** |
||
154 | * Fetch multiple boards and processes the response. |
||
155 | * |
||
156 | * @param Request $request |
||
157 | * @param string[] $fields |
||
158 | * |
||
159 | * @throws RateLimitedReached |
||
160 | * |
||
161 | * @return Response The response |
||
162 | */ |
||
163 | 6 | View Code Duplication | private function fetchMultipleBoards(Request $request, array $fields = null) |
174 | |||
175 | /** |
||
176 | * Fetch multiple users and processes the response. |
||
177 | * |
||
178 | * @param Request $request |
||
179 | * |
||
180 | * @throws RateLimitedReached |
||
181 | * |
||
182 | * @return Response The response |
||
183 | */ |
||
184 | 4 | View Code Duplication | private function fetchMultipleUsers(Request $request) |
194 | |||
195 | /** |
||
196 | * Fetches multiple pins and processes the response. |
||
197 | * |
||
198 | * @param Request $request |
||
199 | * @param $fields array The fields to require. |
||
200 | * |
||
201 | * @throws RateLimitedReached |
||
202 | * |
||
203 | * @return Response The response |
||
204 | */ |
||
205 | 4 | View Code Duplication | private function fetchMultiplePins(Request $request, array $fields = null) |
206 | { |
||
207 | 4 | $fields = $fields ? $fields : Pin::fields(); |
|
208 | 4 | $request->setFields($fields); |
|
209 | |||
210 | return $this->execute($request, function (Response $response) { |
||
211 | $mapper = new Mapper(new Pin()); |
||
212 | |||
213 | return $mapper->toList($response); |
||
214 | 4 | }); |
|
215 | } |
||
216 | |||
217 | /** |
||
218 | * Get a user. |
||
219 | * |
||
220 | * @param string $usernameOrId The username or identifier of the user. |
||
221 | * |
||
222 | * @throws RateLimitedReached |
||
223 | * |
||
224 | * @return Response The response |
||
225 | */ |
||
226 | 4 | public function getUser($usernameOrId) |
|
236 | |||
237 | /** |
||
238 | * Get a board. |
||
239 | * |
||
240 | * @param string $boardId The board id. |
||
241 | * |
||
242 | * @throws RateLimitedReached |
||
243 | * |
||
244 | * @return Response The response |
||
245 | */ |
||
246 | 2 | public function getBoard($boardId) |
|
256 | |||
257 | /** |
||
258 | * Update a board. |
||
259 | * |
||
260 | * @param Board $board The updated board. |
||
261 | * |
||
262 | * @throws RateLimitedReached |
||
263 | * |
||
264 | * @return Response The response |
||
265 | */ |
||
266 | public function updateBoard(Board $board) |
||
267 | { |
||
268 | $params = array(); |
||
269 | |||
270 | if (empty($board->id)) { |
||
271 | throw new InvalidArgumentException('The board id is required.'); |
||
272 | } |
||
273 | |||
274 | View Code Duplication | if (isset($board->name) && empty($board->name) === false) { |
|
275 | $params['name'] = (string) $board->name; |
||
276 | } |
||
277 | |||
278 | View Code Duplication | if (isset($board->description) && empty($board->description) === false) { |
|
279 | $params['description'] = (string) $board->description; |
||
280 | } |
||
281 | |||
282 | $request = new Request('PATCH', sprintf('boards/%s/', $board->id), $params); |
||
283 | |||
284 | return $this->fetchBoard($request); |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * Get the boards of the authenticated user. |
||
289 | * |
||
290 | * @throws RateLimitedReached |
||
291 | * |
||
292 | * @return Response The response |
||
293 | */ |
||
294 | 2 | public function getUserBoards() |
|
300 | |||
301 | /** |
||
302 | * Get the pins of the authenticated user. |
||
303 | * |
||
304 | * @throws RateLimitedReached |
||
305 | * |
||
306 | * @return Response The response |
||
307 | */ |
||
308 | 2 | public function getUserPins() |
|
314 | |||
315 | /** |
||
316 | * Get the authenticated user. |
||
317 | * |
||
318 | * @throws RateLimitedReached |
||
319 | * |
||
320 | * @return Response The response |
||
321 | */ |
||
322 | 2 | public function getCurrentUser() |
|
328 | |||
329 | /** |
||
330 | * Get the followers of the authenticated user. |
||
331 | * |
||
332 | * @throws RateLimitedReached |
||
333 | * |
||
334 | * @return Response The response |
||
335 | */ |
||
336 | 2 | public function getUserFollowers() |
|
342 | |||
343 | /** |
||
344 | * Get the boards that the authenticated user follows. |
||
345 | * |
||
346 | * @throws RateLimitedReached |
||
347 | * |
||
348 | * @return Response The response |
||
349 | */ |
||
350 | 2 | public function getUserFollowingBoards() |
|
356 | |||
357 | /** |
||
358 | * Get the users that the authenticated user follows. |
||
359 | * |
||
360 | * @throws RateLimitedReached |
||
361 | * |
||
362 | * @return Response The response |
||
363 | */ |
||
364 | 2 | public function getUserFollowing() |
|
370 | |||
371 | /** |
||
372 | * Get the interests that the authenticated user follows. |
||
373 | * |
||
374 | * @link https://www.pinterest.com/explore/901179409185 |
||
375 | * |
||
376 | * @throws RateLimitedReached |
||
377 | * |
||
378 | * @return Response The response |
||
379 | */ |
||
380 | 2 | public function getUserInterests() |
|
386 | |||
387 | /** |
||
388 | * Follow a user. |
||
389 | * |
||
390 | * @param string $username The username of the user to follow. |
||
391 | * |
||
392 | * @throws RateLimitedReached |
||
393 | * |
||
394 | * @return Response The response |
||
395 | */ |
||
396 | 4 | View Code Duplication | public function followUser($username) |
412 | |||
413 | /** |
||
414 | * Unfollow a user. |
||
415 | * |
||
416 | * @param string $usernameOrUserId The username or ID of the user to unfollow. |
||
417 | * |
||
418 | * @throws RateLimitedReached |
||
419 | * |
||
420 | * @return Response The response |
||
421 | */ |
||
422 | 4 | View Code Duplication | public function unfollowUser($usernameOrUserId) |
435 | |||
436 | /** |
||
437 | * Follow a board. |
||
438 | * |
||
439 | * @param string $username The username of the user that owns the board |
||
440 | * @param string $boardName The name of the board |
||
441 | * |
||
442 | * @return Response The response |
||
443 | * |
||
444 | * @throws RateLimitedReached |
||
445 | */ |
||
446 | 6 | View Code Duplication | public function followBoard($username, $boardName) |
466 | |||
467 | /** |
||
468 | * Unfollow a board. |
||
469 | * |
||
470 | * @param string $username The username of the user that owns the board |
||
471 | * @param string $boardName The name of the board |
||
472 | * |
||
473 | * @return Response The response |
||
474 | * |
||
475 | * @throws RateLimitedReached |
||
476 | */ |
||
477 | 6 | View Code Duplication | public function unfollowBoard($username, $boardName) |
494 | |||
495 | /** |
||
496 | * Create a board. |
||
497 | * |
||
498 | * @param string $name The board name. |
||
499 | * @param string $description The board description. |
||
500 | * |
||
501 | * @throws RateLimitedReached |
||
502 | * |
||
503 | * @return Response The response |
||
504 | */ |
||
505 | 2 | public function createBoard($name, $description = null) |
|
523 | |||
524 | /** |
||
525 | * Delete a board. |
||
526 | * |
||
527 | * @param int $boardId The board id. |
||
528 | * |
||
529 | * @throws RateLimitedReached |
||
530 | * |
||
531 | * @return Response The response |
||
532 | */ |
||
533 | View Code Duplication | public function deleteBoard($boardId) |
|
534 | { |
||
535 | if (empty($boardId)) { |
||
536 | throw new InvalidArgumentException('The board id should not be empty.'); |
||
537 | } |
||
538 | |||
539 | $request = new Request('DELETE', "boards/{$boardId}/"); |
||
540 | |||
541 | return $this->execute($request); |
||
542 | } |
||
543 | |||
544 | /** |
||
545 | * Create a pin on a board. |
||
546 | * |
||
547 | * @param string $boardId The board id. |
||
548 | * @param string $note The note. |
||
549 | * @param Image $image The image. |
||
550 | * @param string|null $link The link (Optional). |
||
551 | * |
||
552 | * @throws RateLimitedReached |
||
553 | * |
||
554 | * @return Response The response |
||
555 | */ |
||
556 | 8 | public function createPin($boardId, $note, Image $image, $link = null) |
|
587 | |||
588 | /** |
||
589 | * Delete a Pin. |
||
590 | * |
||
591 | * @param string $pinId The id of the pin to delete. |
||
592 | * |
||
593 | * @throws RateLimitedReached |
||
594 | * |
||
595 | * @return Response The response |
||
596 | */ |
||
597 | View Code Duplication | public function deletePin($pinId) |
|
598 | { |
||
599 | if (empty($pinId)) { |
||
600 | throw new InvalidArgumentException('The pin id should not be empty.'); |
||
601 | } |
||
602 | |||
603 | $request = new Request('DELETE', "pins/{$pinId}/"); |
||
604 | |||
605 | return $this->execute($request); |
||
606 | } |
||
607 | |||
608 | /** |
||
609 | * Get the next items for a paged list. |
||
610 | * |
||
611 | * @param PagedList $pagedList |
||
612 | * |
||
613 | * @throws RateLimitedReached |
||
614 | * |
||
615 | * @return Response The response |
||
616 | */ |
||
617 | 2 | public function getNextItems(PagedList $pagedList) |
|
643 | |||
644 | /** |
||
645 | * Build a request to get the next items of a paged list. |
||
646 | * |
||
647 | * @param PagedList $pagedList |
||
648 | * |
||
649 | * @return Request |
||
650 | */ |
||
651 | private function buildRequestForPagedList(PagedList $pagedList) |
||
666 | |||
667 | /** |
||
668 | * Get the pins of a board. |
||
669 | * |
||
670 | * @param string $boardId |
||
671 | * |
||
672 | * @throws RateLimitedReached |
||
673 | * |
||
674 | * @return Response The response |
||
675 | */ |
||
676 | 2 | View Code Duplication | public function getBoardPins($boardId) |
687 | |||
688 | /** |
||
689 | * Get a single pin. |
||
690 | * |
||
691 | * @param string $pinId |
||
692 | * |
||
693 | * @throws RateLimitedReached |
||
694 | * |
||
695 | * @return Response The Response |
||
696 | */ |
||
697 | View Code Duplication | public function getPin($pinId) |
|
708 | |||
709 | /** |
||
710 | * Update a pin. |
||
711 | * |
||
712 | * @param Pin $pin |
||
713 | * |
||
714 | * @throws RateLimitedReached |
||
715 | * |
||
716 | * @return Response The response |
||
717 | */ |
||
718 | 4 | public function updatePin(Pin $pin) |
|
752 | } |
||
753 |
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.