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 ModelStructureProperty 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 ModelStructureProperty, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
99 | class ModelStructureProperty extends StructureProperty |
||
100 | { |
||
101 | /** |
||
102 | * Track the state of loaded metadata for the structure. |
||
103 | * |
||
104 | * @var boolean |
||
105 | */ |
||
106 | private $isStructureFinalized = false; |
||
107 | |||
108 | /** |
||
109 | * The metadata interfaces to use as the structure. |
||
110 | * |
||
111 | * These are paths (PSR-4) to import. |
||
112 | * |
||
113 | * @var array |
||
114 | */ |
||
115 | private $structureInterfaces = []; |
||
116 | |||
117 | /** |
||
118 | * Store the property's structure. |
||
119 | * |
||
120 | * @var MetadataInterface|array|null |
||
121 | */ |
||
122 | private $structureMetadata; |
||
123 | |||
124 | /** |
||
125 | * Store the property's "terminal" structure. |
||
126 | * |
||
127 | * This represents the value of "structure_metadata" key on a property definition. |
||
128 | * This should always be merged last, after the interfaces are imported. |
||
129 | * |
||
130 | * @var MetadataInterface|array|null |
||
131 | */ |
||
132 | private $terminalStructureMetadata; |
||
133 | |||
134 | /** |
||
135 | * Store the property's model prototype. |
||
136 | * |
||
137 | * @var ArrayAccess|DescribableInterface|null |
||
138 | */ |
||
139 | private $structurePrototype; |
||
140 | |||
141 | /** |
||
142 | * The object type of the "structure" collection to use. |
||
143 | * |
||
144 | * @var string |
||
145 | */ |
||
146 | private $structureModelType; |
||
147 | |||
148 | /** |
||
149 | * The class name of the "structure" collection to use. |
||
150 | * |
||
151 | * Must be a fully-qualified PHP namespace and an implementation of {@see ArrayAccess}. |
||
152 | * |
||
153 | * @var string |
||
154 | */ |
||
155 | private $structureModelClass = StructureModel::class; |
||
156 | |||
157 | /** |
||
158 | * Store the factory instance. |
||
159 | * |
||
160 | * @var FactoryInterface |
||
161 | */ |
||
162 | protected $structureModelFactory; |
||
163 | |||
164 | /** |
||
165 | * Retrieve the property's type identifier. |
||
166 | * |
||
167 | * @return string |
||
168 | */ |
||
169 | public function type() |
||
173 | |||
174 | /** |
||
175 | * Retrieve the property's structure. |
||
176 | * |
||
177 | * @return MetadataInterface|null |
||
178 | */ |
||
179 | public function getStructureMetadata() |
||
187 | |||
188 | /** |
||
189 | * Set the property's structure. |
||
190 | * |
||
191 | * @param MetadataInterface|array|null $data The property's structure (fields, data). |
||
192 | * @throws InvalidArgumentException If the structure is invalid. |
||
193 | * @return self |
||
194 | */ |
||
195 | public function setStructureMetadata($data) |
||
221 | |||
222 | /** |
||
223 | * Retrieve the metadata interfaces used by the property as a structure. |
||
224 | * |
||
225 | * @return array |
||
226 | */ |
||
227 | public function getStructureInterfaces() |
||
235 | |||
236 | /** |
||
237 | * Determine if the property has any structure metadata interfaces. |
||
238 | * |
||
239 | * @return boolean |
||
240 | */ |
||
241 | public function hasStructureInterfaces() |
||
245 | |||
246 | /** |
||
247 | * Set the given metadata interfaces for the property to use as a structure. |
||
248 | * |
||
249 | * @param array $interfaces One or more metadata interfaces to use. |
||
250 | * @return self |
||
251 | */ |
||
252 | public function setStructureInterfaces(array $interfaces) |
||
260 | |||
261 | /** |
||
262 | * Add the given metadata interfaces for the property to use as a structure. |
||
263 | * |
||
264 | * @param array $interfaces One or more metadata interfaces to use. |
||
265 | * @return self |
||
266 | */ |
||
267 | public function addStructureInterfaces(array $interfaces) |
||
275 | |||
276 | /** |
||
277 | * Add the given metadata interfaces for the property to use as a structure. |
||
278 | * |
||
279 | * @param string $interface A metadata interface to use. |
||
280 | * @throws InvalidArgumentException If the interface is not a string. |
||
281 | * @return self |
||
282 | */ |
||
283 | public function addStructureInterface($interface) |
||
301 | |||
302 | /** |
||
303 | * Load the property's structure. |
||
304 | * |
||
305 | * @return MetadataInterface |
||
306 | */ |
||
307 | protected function loadStructureMetadata() |
||
346 | |||
347 | /** |
||
348 | * Retrieve a singleton of the structure model for prototyping. |
||
349 | * |
||
350 | * @return ArrayAccess|DescribableInterface |
||
351 | */ |
||
352 | public function structureProto() |
||
366 | |||
367 | /** |
||
368 | * Retrieve the default data-model structure class name. |
||
369 | * |
||
370 | * @return string |
||
371 | */ |
||
372 | public static function getDefaultStructureModelClass() |
||
376 | |||
377 | /** |
||
378 | * Set the class name of the data-model structure. |
||
379 | * |
||
380 | * A model type (kebab-case) is converted to a FQN. |
||
381 | * |
||
382 | * @param string $className The class name of the structure. |
||
383 | * @throws InvalidArgumentException If the class name is invalid. |
||
384 | * @return self |
||
385 | */ |
||
386 | protected function setStructureModelClass($className) |
||
418 | |||
419 | /** |
||
420 | * Determine if the property is using a custom data-model. |
||
421 | * |
||
422 | * @return boolean |
||
423 | */ |
||
424 | public function hasCustomStructureModelClass() |
||
428 | |||
429 | /** |
||
430 | * Retrieve the class name of the data-model structure. |
||
431 | * |
||
432 | * @return string |
||
433 | */ |
||
434 | public function getStructureModelClass() |
||
438 | |||
439 | /** |
||
440 | * Retrieve the class name of the data-model structure. |
||
441 | * |
||
442 | * @return string |
||
443 | */ |
||
444 | public function getStructureModelType() |
||
452 | |||
453 | /** |
||
454 | * Convert the given value into a structure. |
||
455 | * |
||
456 | * Options: |
||
457 | * - `default_data` (_boolean_|_array_) — If TRUE, the default data defined |
||
458 | * in the structure's metadata is merged. If an array, that is merged. |
||
459 | * |
||
460 | * @param mixed $val The value to "structurize". |
||
461 | * @param array|MetadataInterface $options Optional structure options. |
||
462 | * @throws InvalidArgumentException If the options are invalid. |
||
463 | * @return ModelInterface|ModelInterface[] |
||
464 | */ |
||
465 | public function structureVal($val, $options = []) |
||
515 | |||
516 | /** |
||
517 | * Retrieve the structure as a plain array. |
||
518 | * |
||
519 | * @return array |
||
520 | */ |
||
521 | public function toStructure() |
||
525 | |||
526 | /** |
||
527 | * @param null|string $model Model ident. |
||
528 | * @return ArrayAccess|DescribableInterface|mixed |
||
529 | * @throws UnexpectedValueException If the structure is invalid. |
||
530 | */ |
||
531 | View Code Duplication | public function toModel($model = null) |
|
549 | |||
550 | /** |
||
551 | * PropertyInterface::save(). |
||
552 | * @param mixed $val The value, at time of saving. |
||
553 | * @return mixed |
||
554 | */ |
||
555 | public function save($val) |
||
582 | |||
583 | /** |
||
584 | * @param mixed $val The value to to convert for display. |
||
585 | * @param array $options Optional display options. |
||
586 | * @return string |
||
587 | */ |
||
588 | public function displayVal($val, array $options = []) |
||
612 | |||
613 | /** |
||
614 | * Inject dependencies from a DI Container. |
||
615 | * |
||
616 | * @param Container $container A dependencies container instance. |
||
617 | * @return void |
||
618 | */ |
||
619 | protected function setDependencies(Container $container) |
||
625 | |||
626 | /** |
||
627 | * Retrieve the structure model factory. |
||
628 | * |
||
629 | * @throws RuntimeException If the model factory was not previously set. |
||
630 | * @return FactoryInterface |
||
631 | */ |
||
632 | protected function structureModelFactory() |
||
643 | |||
644 | /** |
||
645 | * Set an structure model factory. |
||
646 | * |
||
647 | * @param FactoryInterface $factory The model factory, to create objects. |
||
648 | * @return self |
||
649 | */ |
||
650 | private function setStructureModelFactory(FactoryInterface $factory) |
||
656 | |||
657 | /** |
||
658 | * Parse a metadata identifier from given interface. |
||
659 | * |
||
660 | * Change `\` and `.` to `/` and force lowercase |
||
661 | * |
||
662 | * @param string $interface A metadata interface to convert. |
||
663 | * @return string |
||
664 | */ |
||
665 | protected function parseStructureInterface($interface) |
||
672 | |||
673 | /** |
||
674 | * Create a new metadata object for structures. |
||
675 | * |
||
676 | * Similar to {@see \Charcoal\Model\DescribableTrait::createMetadata()}. |
||
677 | * |
||
678 | * @return MetadataInterface |
||
679 | */ |
||
680 | protected function createStructureMetadata() |
||
685 | |||
686 | /** |
||
687 | * Retrieve the class name of the metadata object. |
||
688 | * |
||
689 | * @return string |
||
690 | */ |
||
691 | protected function getStructureMetadataClass() |
||
695 | |||
696 | /** |
||
697 | * Create a data-model structure. |
||
698 | * |
||
699 | * @todo Add support for simple {@see ArrayAccess} models. |
||
700 | * @throws UnexpectedValueException If the structure is invalid. |
||
701 | * @return ArrayAccess |
||
702 | */ |
||
703 | View Code Duplication | private function createStructureModel() |
|
718 | |||
719 | /** |
||
720 | * Create a data-model structure. |
||
721 | * |
||
722 | * @param MetadataInterface $metadata The model's definition. |
||
723 | * @param array ...$datasets The dataset(s) to modelize. |
||
724 | * @throws UnexpectedValueException If the structure is invalid. |
||
725 | * @return DescribableInterface |
||
726 | */ |
||
727 | private function createStructureModelWith( |
||
750 | } |
||
751 |