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 Collection 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 Collection, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | class Collection implements CollectionContract { |
||
17 | |||
18 | /** |
||
19 | * Collection elements. |
||
20 | * |
||
21 | * @var array |
||
22 | */ |
||
23 | protected $elements = array(); |
||
24 | |||
25 | /** |
||
26 | * Collection type to enforce. |
||
27 | * |
||
28 | * @var Type |
||
29 | */ |
||
30 | private $type; |
||
31 | |||
32 | /** |
||
33 | * Where Collection is in loop. |
||
34 | * |
||
35 | * @var int |
||
36 | */ |
||
37 | protected $position = 0; |
||
38 | |||
39 | /** |
||
40 | * Collection constructor. |
||
41 | * |
||
42 | * @param string $type |
||
43 | * @param array $elements |
||
44 | */ |
||
45 | 312 | public function __construct( $type, array $elements = array() ) { |
|
62 | |||
63 | /** |
||
64 | * {@inheritdoc} |
||
65 | * |
||
66 | * @return string |
||
67 | */ |
||
68 | 144 | public function get_type() { |
|
71 | |||
72 | /** |
||
73 | * {@inheritdoc} |
||
74 | * |
||
75 | * @param mixed $element |
||
76 | * |
||
77 | * @return Collection |
||
78 | * |
||
79 | * @throws InvalidArgumentException |
||
80 | */ |
||
81 | 51 | public function add( $element ) { |
|
96 | |||
97 | /** |
||
98 | * {@inheritdoc} |
||
99 | * |
||
100 | * @return Collection |
||
101 | */ |
||
102 | 6 | public function clear() { |
|
105 | |||
106 | /** |
||
107 | * {@inheritdoc} |
||
108 | * |
||
109 | * @param callable $condition Condition to satisfy. |
||
110 | * |
||
111 | * @return bool |
||
112 | */ |
||
113 | 9 | public function contains( $condition ) { |
|
116 | |||
117 | /** |
||
118 | * {@inheritdoc} |
||
119 | * |
||
120 | * @param callable $condition Condition to satisfy. |
||
121 | * |
||
122 | * @return mixed |
||
123 | */ |
||
124 | 15 | public function find( $condition ) { |
|
129 | |||
130 | /** |
||
131 | * {@inheritdoc} |
||
132 | * |
||
133 | * @param callable $condition Condition to satisfy. |
||
134 | * |
||
135 | * @return int |
||
136 | */ |
||
137 | 21 | public function find_index( $condition ) { |
|
149 | |||
150 | /** |
||
151 | * Fetches the element at the provided index. |
||
152 | * |
||
153 | * @param int $index Index to get element from. |
||
154 | * |
||
155 | * @return mixed |
||
156 | * |
||
157 | * @throws OutOfRangeException |
||
158 | */ |
||
159 | 99 | public function at( $index ) { |
|
164 | |||
165 | /** |
||
166 | * {@inheritdoc} |
||
167 | * |
||
168 | * @param int $index Index to check for existence. |
||
169 | * |
||
170 | * @return bool |
||
171 | * |
||
172 | * @throws InvalidArgumentException |
||
173 | */ |
||
174 | 111 | public function index_exists( $index ) { |
|
185 | |||
186 | /** |
||
187 | * {@inheritdoc} |
||
188 | * |
||
189 | * @param callable $condition Condition to satisfy. |
||
190 | * |
||
191 | * @return Collection |
||
192 | */ |
||
193 | 6 | public function filter( $condition ) { |
|
204 | /** |
||
205 | * {@inheritdoc} |
||
206 | * |
||
207 | * @param callable $condition Condition to satisfy. |
||
208 | * |
||
209 | * @return mixed |
||
210 | */ |
||
211 | 6 | public function find_last( $condition ) { |
|
216 | |||
217 | /** |
||
218 | * {@inheritdoc} |
||
219 | * |
||
220 | * @param callable $condition |
||
221 | * @return int |
||
222 | */ |
||
223 | 12 | public function find_last_index( $condition ) { |
|
235 | |||
236 | /** |
||
237 | * {@inheritdoc} |
||
238 | * |
||
239 | * @param int $start Begining index to slice from. |
||
240 | * @param int $end End index to slice to. |
||
241 | * |
||
242 | * @return Collection |
||
243 | * |
||
244 | * @throws InvalidArgumentException |
||
245 | */ |
||
246 | 81 | public function slice( $start, $end ) { |
|
267 | |||
268 | /** |
||
269 | * {@inheritdoc} |
||
270 | * |
||
271 | * @param int $index Index to start at. |
||
272 | * @param mixed $element Element to insert. |
||
273 | * |
||
274 | * @return Collection |
||
275 | * |
||
276 | * @throws InvalidArgumentException |
||
277 | * @throws OutOfRangeException |
||
278 | */ |
||
279 | 3 | public function insert( $index, $element ) { |
|
290 | |||
291 | /** |
||
292 | * {@inheritdoc} |
||
293 | * |
||
294 | * @param int $index Index to start insertion at. |
||
295 | * @param array $elements Elements in insert. |
||
296 | * |
||
297 | * @return Collection |
||
298 | * |
||
299 | * @throws OutOfRangeException |
||
300 | */ |
||
301 | 3 | public function insert_range( $index, array $elements ) { |
|
317 | |||
318 | /** |
||
319 | * {@inheritdoc} |
||
320 | * |
||
321 | * @param callable $condition Condition to satisfy. |
||
322 | * |
||
323 | * @return Collection |
||
324 | */ |
||
325 | 2 | public function reject( $condition ) { |
|
332 | |||
333 | /** |
||
334 | * {@inheritdoc} |
||
335 | * |
||
336 | * @param int $index Index to remove. |
||
337 | * |
||
338 | * @return Collection |
||
339 | * |
||
340 | * @throws OutOfRangeException |
||
341 | */ |
||
342 | 3 | public function remove_at( $index ) { |
|
354 | /** |
||
355 | * {@inheritdoc} |
||
356 | * |
||
357 | * @return Collection |
||
358 | */ |
||
359 | 3 | public function reverse() { |
|
364 | |||
365 | /** |
||
366 | * {@inheritdoc} |
||
367 | * |
||
368 | * @param callable $callback Sort callback. |
||
369 | * |
||
370 | * @return Collection |
||
371 | */ |
||
372 | 3 | public function sort( $callback ) { |
|
377 | |||
378 | /** |
||
379 | * {@inheritdoc} |
||
380 | * |
||
381 | * @return array |
||
382 | */ |
||
383 | 30 | public function to_array() { |
|
386 | |||
387 | /** |
||
388 | * {@inheritdoc} |
||
389 | * |
||
390 | * @param callable $callable Reducer function. |
||
391 | * |
||
392 | * @param null $initial Initial reducer value. |
||
393 | * |
||
394 | * @return mixed |
||
395 | */ |
||
396 | 3 | public function reduce( $callable, $initial = null ) { |
|
399 | |||
400 | /** |
||
401 | * {@inheritdoc} |
||
402 | * |
||
403 | * @param callable $condition Condition callback. |
||
404 | * |
||
405 | * @return bool |
||
406 | */ |
||
407 | 6 | public function every( $condition ) { |
|
421 | |||
422 | /** |
||
423 | * {@inheritdoc} |
||
424 | * |
||
425 | * @param int $num Number of elements to drop. |
||
426 | * |
||
427 | * @return Collection |
||
428 | * |
||
429 | * @throws InvalidArgumentException |
||
430 | */ |
||
431 | 18 | public function drop( $num ) { |
|
438 | |||
439 | /** |
||
440 | * {@inheritdoc} |
||
441 | * |
||
442 | * @param int $num Number of elements to drop. |
||
443 | * |
||
444 | * @return Collection |
||
445 | * |
||
446 | * @throws InvalidArgumentException |
||
447 | */ |
||
448 | 9 | public function drop_right( $num ) { |
|
453 | |||
454 | /** |
||
455 | * {@inheritdoc} |
||
456 | * |
||
457 | * @param callable $condition Condition callback. |
||
458 | * |
||
459 | * @return Collection |
||
460 | */ |
||
461 | 9 | public function drop_while( $condition ) { |
|
465 | /** |
||
466 | * {@inheritdoc} |
||
467 | * |
||
468 | * @return Collection |
||
469 | * |
||
470 | * @throws InvalidArgumentException |
||
471 | */ |
||
472 | 3 | public function tail() { |
|
475 | |||
476 | /** |
||
477 | * {@inheritdoc} |
||
478 | * |
||
479 | * @param int $num Number of elements to take. |
||
480 | * |
||
481 | * @return Collection |
||
482 | * |
||
483 | * @throws InvalidArgumentException |
||
484 | */ |
||
485 | 18 | public function take( $num ) { |
|
488 | |||
489 | /** |
||
490 | * {@inheritdoc} |
||
491 | * |
||
492 | * @param int $num Number of elements to take. |
||
493 | * |
||
494 | * @return Collection |
||
495 | * |
||
496 | * @throws InvalidArgumentException |
||
497 | */ |
||
498 | 3 | public function take_right( $num ) { |
|
501 | |||
502 | /** |
||
503 | * {@inheritdoc} |
||
504 | * |
||
505 | * @param callable $condition Callback function. |
||
506 | * |
||
507 | * @return Collection |
||
508 | */ |
||
509 | 3 | public function take_while( $condition ) { |
|
514 | |||
515 | /** |
||
516 | * {@inheritdoc} |
||
517 | * |
||
518 | * @param callable $callable Callback function. |
||
519 | */ |
||
520 | 3 | public function each( $callable ) { |
|
525 | |||
526 | /** |
||
527 | * {@inheritdoc} |
||
528 | * |
||
529 | * @param callable $callable Callback function. |
||
530 | * |
||
531 | * @return Collection |
||
532 | */ |
||
533 | 18 | public function map( $callable ) { |
|
552 | |||
553 | /** |
||
554 | * {@inheritdoc} |
||
555 | * |
||
556 | * @param callable $callable Reducer function. |
||
557 | * @param null $initial Initial value. |
||
558 | * |
||
559 | * @return mixed |
||
560 | */ |
||
561 | 15 | public function reduce_right( $callable, $initial = null ) { |
|
562 | 15 | return array_reduce( |
|
563 | 15 | array_reverse( $this->elements ), |
|
564 | 10 | $callable, |
|
565 | $initial |
||
566 | 10 | ); |
|
567 | } |
||
568 | |||
569 | /** |
||
570 | * {@inheritdoc} |
||
571 | * |
||
572 | * @return Collection |
||
573 | */ |
||
574 | 3 | public function shuffle() { |
|
580 | |||
581 | /** |
||
582 | * {@inheritdoc} |
||
583 | * |
||
584 | * @param array|Collection $elements Array of elements to merge. |
||
585 | * |
||
586 | * @return Collection |
||
587 | * |
||
588 | * @throws InvalidArgumentException |
||
589 | */ |
||
590 | 12 | View Code Duplication | public function merge( $elements ) { |
605 | |||
606 | /** |
||
607 | * {@inheritdoc} |
||
608 | * |
||
609 | * @return mixed |
||
610 | * |
||
611 | * @throws OutOfBoundsException |
||
612 | */ |
||
613 | 6 | public function first() { |
|
620 | |||
621 | /** |
||
622 | * {@inheritdoc} |
||
623 | * |
||
624 | * @return mixed |
||
625 | * |
||
626 | * @throws OutOfBoundsException |
||
627 | */ |
||
628 | 6 | public function last() { |
|
635 | |||
636 | /** |
||
637 | * {@inheritdoc} |
||
638 | * |
||
639 | * @return int |
||
640 | */ |
||
641 | 168 | public function count() { |
|
644 | |||
645 | /** |
||
646 | * {@inheritDoc} |
||
647 | * |
||
648 | * @return array |
||
649 | */ |
||
650 | public function serialize() { |
||
659 | |||
660 | /** |
||
661 | * Return the current element. |
||
662 | * |
||
663 | * @return mixed |
||
664 | */ |
||
665 | 3 | public function current() { |
|
668 | |||
669 | /** |
||
670 | * Move forward to next element. |
||
671 | */ |
||
672 | 3 | public function next() { |
|
675 | |||
676 | /** |
||
677 | * Return the key of the current element. |
||
678 | * |
||
679 | * @return mixed |
||
680 | */ |
||
681 | 3 | public function key() { |
|
684 | |||
685 | /** |
||
686 | * Checks if current position is valid. |
||
687 | * |
||
688 | * @return bool |
||
689 | */ |
||
690 | 3 | public function valid() { |
|
693 | |||
694 | /** |
||
695 | * Rewind the Iterator to the first element. |
||
696 | */ |
||
697 | 3 | public function rewind() { |
|
700 | |||
701 | /** |
||
702 | * Creates a new instance of the Collection |
||
703 | * from a trusted set of elements. |
||
704 | * |
||
705 | * @param array $elements Array of elements to pass into new collection. |
||
706 | * @param null|mixed $type |
||
707 | * |
||
708 | * @return static |
||
709 | */ |
||
710 | 108 | protected function new_from_trusted( array $elements, $type = null ) { |
|
716 | |||
717 | /** |
||
718 | * Sets the elements without validating them. |
||
719 | * |
||
720 | * @param array $elements Pre-validated elements to set. |
||
721 | */ |
||
722 | 153 | protected function set_from_trusted( array $elements ) { |
|
725 | |||
726 | /** |
||
727 | * Number of elements true for the condition. |
||
728 | * |
||
729 | * @param callable $condition Condition to check. |
||
730 | * @return int |
||
731 | */ |
||
732 | 12 | protected function count_while_true( $condition ) { |
|
744 | |||
745 | /** |
||
746 | * Validates a number to be used as an index. |
||
747 | * |
||
748 | * @param integer $index The number to be validated as an index. |
||
749 | * |
||
750 | * @throws OutOfRangeException |
||
751 | */ |
||
752 | 102 | protected function validate_index( $index ) { |
|
759 | } |
||
760 |
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.