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 Complex_Field 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 Complex_Field, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
15 | class Complex_Field extends Field { |
||
16 | const LAYOUT_GRID = 'grid'; // default |
||
17 | const LAYOUT_LIST = 'list'; // deprecated |
||
18 | const LAYOUT_TABBED = 'tabbed'; // deprecated |
||
19 | const LAYOUT_TABBED_HORIZONTAL = 'tabbed-horizontal'; |
||
20 | const LAYOUT_TABBED_VERTICAL = 'tabbed-vertical'; |
||
21 | |||
22 | protected $fields = array(); |
||
23 | protected $values = array(); |
||
24 | protected $groups = array(); |
||
25 | |||
26 | protected $layout = self::LAYOUT_GRID; |
||
27 | public $static = false; |
||
28 | protected $values_min = -1; |
||
29 | protected $values_max = -1; |
||
30 | |||
31 | public $labels = array( |
||
32 | 'singular_name' => 'Entry', |
||
33 | 'plural_name' => 'Entries', |
||
34 | ); |
||
35 | |||
36 | /** |
||
37 | * Initialization tasks. |
||
38 | */ |
||
39 | public function init() { |
||
51 | |||
52 | /** |
||
53 | * Add a set/group of fields. |
||
54 | * |
||
55 | * @return $this |
||
56 | */ |
||
57 | public function add_fields() { |
||
90 | |||
91 | /** |
||
92 | * Set the group label Underscore template. |
||
93 | * |
||
94 | * @param string|callable $template |
||
95 | * @return $this |
||
96 | */ |
||
97 | public function set_header_template( $template ) { |
||
116 | |||
117 | /** |
||
118 | * Retrieve all groups of fields. |
||
119 | * |
||
120 | * @return array $fields |
||
121 | */ |
||
122 | public function get_fields() { |
||
133 | |||
134 | /** |
||
135 | * Set the field labels. |
||
136 | * Currently supported values: |
||
137 | * - singular_name - the singular entry label |
||
138 | * - plural_name - the plural entries label |
||
139 | * |
||
140 | * @param array $labels Labels |
||
141 | */ |
||
142 | public function setup_labels( $labels ) { |
||
146 | |||
147 | /** |
||
148 | * Set the datastore of this field. |
||
149 | * |
||
150 | * @param Datastore_Interface $store |
||
151 | */ |
||
152 | public function set_datastore( Datastore_Interface $store ) { |
||
159 | |||
160 | /** |
||
161 | * Load the field value from an input array based on it's name. |
||
162 | * |
||
163 | * @param array $input (optional) Array of field names and values. Defaults to $_POST |
||
164 | **/ |
||
165 | public function set_value_from_input( $input = null ) { |
||
221 | |||
222 | /** |
||
223 | * Load all groups of fields and their data. |
||
224 | */ |
||
225 | public function load() { |
||
229 | |||
230 | /** |
||
231 | * Save all contained groups of fields. |
||
232 | */ |
||
233 | public function save() { |
||
242 | |||
243 | /** |
||
244 | * Delete the values of all contained fields. |
||
245 | */ |
||
246 | public function delete() { |
||
249 | |||
250 | /** |
||
251 | * Load and parse the field data. |
||
252 | */ |
||
253 | public function load_values() { |
||
256 | |||
257 | /** |
||
258 | * Load and parse the field data from the database. |
||
259 | */ |
||
260 | public function load_values_from_db() { |
||
267 | |||
268 | /** |
||
269 | * Load and parse a raw set of field data. |
||
270 | * |
||
271 | * @param array $values Raw data entries |
||
272 | * @return array Processed data entries |
||
273 | */ |
||
274 | public function load_values_from_array( $values ) { |
||
294 | |||
295 | /** |
||
296 | * Parse groups of raw field data into the actual field hierarchy. |
||
297 | * |
||
298 | * @param array $group_rows Group rows |
||
299 | */ |
||
300 | public function process_loaded_values( $group_rows ) { |
||
359 | |||
360 | /** |
||
361 | * Retrieve the field values. |
||
362 | * @return array |
||
363 | */ |
||
364 | public function get_values() { |
||
367 | |||
368 | /** |
||
369 | * Generate and set the field prefix. |
||
370 | * @param string $prefix |
||
371 | */ |
||
372 | public function set_prefix( $prefix ) { |
||
379 | |||
380 | /** |
||
381 | * Returns an array that holds the field data, suitable for JSON representation. |
||
382 | * This data will be available in the Underscore template and the Backbone Model. |
||
383 | * |
||
384 | * @param bool $load Should the value be loaded from the database or use the value from the current instance. |
||
385 | * @return array |
||
386 | */ |
||
387 | public function to_json( $load ) { |
||
429 | |||
430 | /** |
||
431 | * The main Underscore template. |
||
432 | */ |
||
433 | public function template() { |
||
486 | |||
487 | /** |
||
488 | * The Underscore template for the complex field group. |
||
489 | */ |
||
490 | public function template_group() { |
||
491 | ?> |
||
492 | <div id="carbon-{{{ complex_name }}}-complex-container" class="carbon-row carbon-group-row {{ static ? 'static' : '' }}" data-group-id="{{ id }}"> |
||
493 | <input type="hidden" name="{{{ complex_name + '[' + index + ']' }}}[group]" value="{{ name }}" /> |
||
494 | |||
495 | <div class="carbon-drag-handle"> |
||
496 | <span class="group-number">{{{ order + 1 }}}</span><span class="group-name">{{{ label_template || label }}}</span> |
||
497 | </div> |
||
498 | |||
499 | <div class="carbon-group-actions"> |
||
500 | <a class="carbon-btn-collapse" href="#" title="<?php esc_attr_e( 'Collapse/Expand', 'carbon_fields' ); ?>"> |
||
501 | <?php _e( 'Collapse/Expand', 'carbon_fields' ); ?> |
||
502 | </a> |
||
503 | |||
504 | <# if (!static) { #> |
||
505 | <a class="carbon-btn-duplicate" href="#" title="<?php esc_attr_e( 'Clone', 'carbon_fields' ); ?>"> |
||
506 | <?php _e( 'Clone', 'carbon_fields' ); ?> |
||
507 | </a> |
||
508 | |||
509 | <a class="carbon-btn-remove" href="#" title="<?php esc_attr_e( 'Remove', 'carbon_fields' ); ?>"> |
||
510 | <?php _e( 'Remove', 'carbon_fields' ); ?> |
||
511 | </a> |
||
512 | <# } #> |
||
513 | </div> |
||
514 | |||
515 | <div class="fields-container"> |
||
516 | <# _.each(fields, function(field) { #> |
||
517 | <div class="carbon-row carbon-subrow subrow-{{{ field.type }}} {{{ field.classes.join(' ') }}}"> |
||
518 | <label for="{{{ complex_id + '-' + field.id + '-' + index }}}"> |
||
519 | {{ field.label }} |
||
520 | |||
521 | <# if (field.required) { #> |
||
522 | <span class="carbon-required">*</span> |
||
523 | <# } #> |
||
524 | </label> |
||
525 | |||
526 | <div class="field-holder {{{ complex_id + '-' + field.id + '-' + index }}}"></div> |
||
527 | |||
528 | <# if (field.help_text) { #> |
||
529 | <em class="help-text"> |
||
530 | {{{ field.help_text }}} |
||
531 | </em> |
||
532 | <# } #> |
||
533 | |||
534 | <em class="carbon-error"></em> |
||
535 | </div> |
||
536 | <# }) #> |
||
537 | </div> |
||
538 | </div> |
||
539 | <?php |
||
540 | } |
||
541 | |||
542 | /** |
||
543 | * The Underscore template for the group item tab. |
||
544 | */ |
||
545 | public function template_group_tab_item() { |
||
560 | |||
561 | /** |
||
562 | * Modify the layout of this field. |
||
563 | * |
||
564 | * @param string $layout |
||
565 | * @return Complex_Field |
||
566 | */ |
||
567 | public function set_layout( $layout ) { |
||
598 | |||
599 | /** |
||
600 | * Set the complex fields to static. |
||
601 | * If you set the type to static, there will be no options to add or remove any group. |
||
602 | * Min and max values are going to be ignored. |
||
603 | * |
||
604 | * @param string $static |
||
605 | * @return Complex_Field |
||
606 | */ |
||
607 | public function set_static($static) { |
||
611 | |||
612 | /** |
||
613 | * Set the minimum number of entries. |
||
614 | * |
||
615 | * @param int $min |
||
616 | * @return Complex_Field |
||
617 | */ |
||
618 | public function set_min( $min ) { |
||
622 | |||
623 | /** |
||
624 | * Get the minimum number of entries. |
||
625 | * |
||
626 | * @return int $min |
||
627 | */ |
||
628 | public function get_min() { |
||
631 | |||
632 | /** |
||
633 | * Set the maximum number of entries. |
||
634 | * |
||
635 | * @param int $max |
||
636 | * @return Complex_Field |
||
637 | */ |
||
638 | public function set_max( $max ) { |
||
642 | |||
643 | /** |
||
644 | * Get the maximum number of entries. |
||
645 | * |
||
646 | * @return int $max |
||
647 | */ |
||
648 | public function get_max() { |
||
651 | |||
652 | /** |
||
653 | * Retrieve the groups of this field. |
||
654 | * |
||
655 | * @return array |
||
656 | */ |
||
657 | public function get_group_names() { |
||
660 | |||
661 | /** |
||
662 | * Retrieve a group by its name. |
||
663 | * |
||
664 | * @param string $group_name Group name |
||
665 | * @return Group_Field $group_object Group object |
||
666 | */ |
||
667 | public function get_group_by_name( $group_name ) { |
||
678 | } |
||
679 |
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.