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 EntityMetadata 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 EntityMetadata, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
12 | class EntityMetadata implements ApiMetadata |
||
13 | { |
||
14 | /** |
||
15 | * The ReflectionProperty instances of the mapped class. |
||
16 | * |
||
17 | * @var \ReflectionProperty[] |
||
18 | */ |
||
19 | public $reflFields = []; |
||
20 | /** @var string */ |
||
21 | public $name; |
||
22 | /** @var string */ |
||
23 | public $namespace; |
||
24 | /** @var string */ |
||
25 | public $rootEntityName; |
||
26 | /** @var string[] */ |
||
27 | public $identifier = []; |
||
28 | /** @var array */ |
||
29 | public $fields = []; |
||
30 | /** @var array */ |
||
31 | public $associations = []; |
||
32 | /** @var string */ |
||
33 | public $repositoryClass = EntityRepository::class; |
||
34 | /** @var \ReflectionClass */ |
||
35 | public $reflClass; |
||
36 | /** @var MethodProviderInterface */ |
||
37 | public $methodProvider; |
||
38 | /** @var string */ |
||
39 | public $clientName; |
||
40 | /** @var string */ |
||
41 | public $apiFactory; |
||
42 | /** @var string[] */ |
||
43 | public $apiFieldNames = []; |
||
44 | /** @var string[] */ |
||
45 | public $fieldNames = []; |
||
46 | /** @var bool */ |
||
47 | public $isMappedSuperclass = false; |
||
48 | /** @var bool */ |
||
49 | public $containsForeignIdentifier; |
||
50 | /** @var bool */ |
||
51 | public $isIdentifierComposite = false; |
||
52 | /** @var int */ |
||
53 | public $generatorType = self::GENERATOR_TYPE_NATURAL; |
||
54 | /** @var string[] */ |
||
55 | public $discriminatorField; |
||
56 | /** @var string[] */ |
||
57 | public $discriminatorMap; |
||
58 | /** @var string */ |
||
59 | public $discriminatorValue; |
||
60 | /** @var string[] */ |
||
61 | public $subclasses = []; |
||
62 | /** @var InstantiatorInterface */ |
||
63 | private $instantiator; |
||
64 | /** @var int */ |
||
65 | private $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; |
||
66 | |||
67 | /** |
||
68 | * Initializes a new ClassMetadata instance that will hold the object-relational mapping |
||
69 | * metadata of the class with the given name. |
||
70 | * |
||
71 | * @param string $entityName The name of the entity class the new instance is used for. |
||
72 | */ |
||
73 | 28 | public function __construct($entityName) |
|
78 | |||
79 | /** |
||
80 | * @return boolean |
||
81 | */ |
||
82 | public function containsForeignIdentifier() |
||
86 | |||
87 | /** {@inheritdoc} */ |
||
88 | 11 | public function getReflectionProperties() |
|
92 | |||
93 | /** |
||
94 | * {@inheritdoc} |
||
95 | */ |
||
96 | 25 | public function getReflectionProperty($name) |
|
104 | |||
105 | /** {@inheritdoc} */ |
||
106 | 28 | public function getName() |
|
110 | |||
111 | /** {@inheritdoc} */ |
||
112 | 27 | public function getMethodContainer() |
|
116 | |||
117 | /** {@inheritdoc} */ |
||
118 | 18 | public function getRepositoryClass() |
|
122 | |||
123 | /** {@inheritdoc} */ |
||
124 | 2 | public function getIdentifier() |
|
128 | |||
129 | 14 | public function setIdentifier($identifier) |
|
134 | |||
135 | /** {@inheritdoc} */ |
||
136 | 18 | public function getReflectionClass() |
|
144 | |||
145 | /** {@inheritdoc} */ |
||
146 | 7 | public function isIdentifier($fieldName) |
|
150 | |||
151 | /** {@inheritdoc} */ |
||
152 | 2 | public function hasField($fieldName) |
|
156 | |||
157 | /** {@inheritdoc} */ |
||
158 | 18 | public function getFieldNames() |
|
162 | |||
163 | /** {@inheritdoc} */ |
||
164 | 26 | public function hasAssociation($fieldName) |
|
168 | |||
169 | /** {@inheritdoc} */ |
||
170 | 26 | public function getAssociationNames() |
|
174 | |||
175 | /** {@inheritdoc} */ |
||
176 | 12 | public function isSingleValuedAssociation($fieldName) |
|
180 | |||
181 | /** {@inheritdoc} */ |
||
182 | 16 | public function isCollectionValuedAssociation($fieldName) |
|
186 | |||
187 | /** {@inheritdoc} */ |
||
188 | 21 | public function getIdentifierFieldNames() |
|
192 | |||
193 | /** {@inheritdoc} */ |
||
194 | 26 | public function getTypeOfField($fieldName) |
|
198 | |||
199 | /** {@inheritdoc} */ |
||
200 | 12 | public function getAssociationTargetClass($assocName) |
|
204 | |||
205 | /** {@inheritdoc} */ |
||
206 | public function isAssociationInverseSide($assocName) |
||
212 | |||
213 | /** {@inheritdoc} */ |
||
214 | public function getAssociationMappedByTargetField($assocName) |
||
218 | |||
219 | /** {@inheritdoc} */ |
||
220 | 22 | public function getIdentifierValues($object) |
|
241 | |||
242 | /** {@inheritdoc} */ |
||
243 | 28 | public function wakeupReflection(ReflectionService $reflService) |
|
259 | |||
260 | /** {@inheritdoc} */ |
||
261 | 28 | public function initializeReflection(ReflectionService $reflService) |
|
269 | |||
270 | /** {@inheritdoc} */ |
||
271 | 26 | public function getApiFactory() |
|
279 | |||
280 | /** {@inheritdoc} */ |
||
281 | 27 | public function getClientName() |
|
289 | |||
290 | 28 | public function mapField(array $mapping) |
|
296 | |||
297 | /** {@inheritdoc} */ |
||
298 | 26 | public function getFieldMapping($fieldName) |
|
306 | |||
307 | /** {@inheritdoc} */ |
||
308 | 26 | public function getFieldOptions($fieldName) |
|
312 | |||
313 | /** {@inheritdoc} */ |
||
314 | 17 | public function getAssociationMapping($fieldName) |
|
322 | |||
323 | 2 | public function setCustomRepositoryClass($customRepositoryClassName) |
|
327 | |||
328 | /** |
||
329 | * @internal |
||
330 | * |
||
331 | * @param array $mapping |
||
332 | * |
||
333 | * @return void |
||
334 | */ |
||
335 | 14 | public function addInheritedFieldMapping(array $mapping) |
|
341 | |||
342 | /** {@inheritdoc} */ |
||
343 | 7 | public function getFieldName($apiFieldName) |
|
347 | |||
348 | /** {@inheritdoc} */ |
||
349 | 26 | public function getApiFieldName($fieldName) |
|
353 | |||
354 | public function hasApiField($apiFieldName) |
||
358 | |||
359 | 17 | public function mapOneToMany(array $mapping) |
|
365 | |||
366 | 17 | public function mapManyToOne(array $mapping) |
|
372 | |||
373 | public function mapOneToOne(array $mapping) |
||
379 | |||
380 | /** {@inheritdoc} */ |
||
381 | 17 | public function newInstance() |
|
385 | |||
386 | 8 | public function isIdentifierComposite() |
|
390 | |||
391 | /** {@inheritdoc} */ |
||
392 | 28 | public function getRootEntityName() |
|
396 | |||
397 | /** |
||
398 | * Populates the entity identifier of an entity. |
||
399 | * |
||
400 | * @param object $entity |
||
401 | * @param array $id |
||
402 | * |
||
403 | * @return void |
||
404 | */ |
||
405 | public function assignIdentifier($entity, array $id) |
||
411 | |||
412 | 10 | public function addInheritedAssociationMapping(array $mapping) |
|
418 | |||
419 | /** {@inheritdoc} */ |
||
420 | 7 | public function getSubclasses() |
|
424 | |||
425 | /** {@inheritdoc} */ |
||
426 | 7 | public function getAssociationMappings() |
|
430 | |||
431 | 3 | public function isReadOnly() |
|
435 | |||
436 | /** |
||
437 | * Sets the change tracking policy used by this class. |
||
438 | * |
||
439 | * @param integer $policy |
||
440 | * |
||
441 | * @return void |
||
442 | */ |
||
443 | public function setChangeTrackingPolicy($policy) |
||
447 | |||
448 | /** |
||
449 | * Whether the change tracking policy of this class is "deferred explicit". |
||
450 | * |
||
451 | * @return boolean |
||
452 | */ |
||
453 | public function isChangeTrackingDeferredExplicit() |
||
457 | |||
458 | /** |
||
459 | * Whether the change tracking policy of this class is "deferred implicit". |
||
460 | * |
||
461 | * @return boolean |
||
462 | */ |
||
463 | 3 | public function isChangeTrackingDeferredImplicit() |
|
467 | |||
468 | /** |
||
469 | * Whether the change tracking policy of this class is "notify". |
||
470 | * |
||
471 | * @return boolean |
||
472 | */ |
||
473 | public function isChangeTrackingNotify() |
||
477 | |||
478 | 28 | public function mapIdentifier(array $mapping) |
|
484 | |||
485 | /** |
||
486 | * Sets the type of Id generator to use for the mapped class. |
||
487 | * |
||
488 | * @param int $generatorType |
||
489 | * |
||
490 | * @return void |
||
491 | */ |
||
492 | 28 | public function setIdGeneratorType($generatorType) |
|
496 | |||
497 | 7 | public function isIdentifierNatural() |
|
501 | |||
502 | 7 | public function isIdentifierRemote() |
|
506 | |||
507 | /** |
||
508 | * Populates the entity identifier of an entity. |
||
509 | * |
||
510 | * @param object $entity |
||
511 | * @param array $id |
||
512 | * |
||
513 | * @return void |
||
514 | * |
||
515 | * @todo Rename to assignIdentifier() |
||
516 | */ |
||
517 | public function setIdentifierValues($entity, array $id) |
||
523 | |||
524 | 28 | public function isRootEntity() |
|
528 | |||
529 | /** |
||
530 | * Adds one entry of the discriminator map with a new class and corresponding name. |
||
531 | * |
||
532 | * @param string $name |
||
533 | * @param string $className |
||
534 | * |
||
535 | * @return void |
||
536 | * |
||
537 | * @throws MappingException |
||
538 | */ |
||
539 | 28 | public function addDiscriminatorMapClass($name, $className) |
|
565 | |||
566 | /** |
||
567 | * @param string|null $className |
||
568 | * |
||
569 | * @return string|null null if the input value is null |
||
570 | */ |
||
571 | 28 | public function fullyQualifiedClassName($className) |
|
582 | |||
583 | /** {@inheritdoc} */ |
||
584 | 26 | public function getDiscriminatorField() |
|
588 | |||
589 | /** |
||
590 | * Sets the discriminator column definition. |
||
591 | * |
||
592 | * @param array $columnDef |
||
593 | * |
||
594 | * @return void |
||
595 | * |
||
596 | * @throws MappingException |
||
597 | * |
||
598 | * @see getDiscriminatorColumn() |
||
599 | */ |
||
600 | 14 | public function setDiscriminatorField(array $columnDef = null) |
|
621 | |||
622 | /** {@inheritdoc} */ |
||
623 | 18 | public function getDiscriminatorMap() |
|
627 | |||
628 | /** |
||
629 | * Sets the discriminator values used by this class. |
||
630 | * |
||
631 | * @param array $map |
||
632 | * |
||
633 | * @return void |
||
634 | */ |
||
635 | 28 | public function setDiscriminatorMap(array $map) |
|
641 | |||
642 | /** {@inheritdoc} */ |
||
643 | 19 | public function getDiscriminatorValue() |
|
647 | |||
648 | 1 | public function mapManyToMany($mapping) |
|
654 | |||
655 | /** |
||
656 | * Validates & completes the basic mapping information that is common to all |
||
657 | * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). |
||
658 | * |
||
659 | * @param array $mapping The mapping. |
||
660 | * |
||
661 | * @return array The updated mapping. |
||
662 | * |
||
663 | * @throws MappingException If something is wrong with the mapping. |
||
664 | */ |
||
665 | 18 | protected function validateAndCompleteAssociationMapping(array $mapping) |
|
758 | |||
759 | 18 | private function storeMapping(array $mapping) |
|
767 | |||
768 | 28 | private function validateAndCompleteFieldMapping(array &$mapping) |
|
796 | |||
797 | /** |
||
798 | * @param string $fieldName |
||
799 | * |
||
800 | * @throws MappingException |
||
801 | */ |
||
802 | 28 | private function assertFieldNotMapped($fieldName) |
|
811 | |||
812 | /** |
||
813 | * @param array $mapping |
||
814 | * |
||
815 | * @return array |
||
816 | * @throws MappingException |
||
817 | * @throws \InvalidArgumentException |
||
818 | */ |
||
819 | 17 | private function validateAndCompleteOneToManyMapping(array $mapping) |
|
835 | |||
836 | /** |
||
837 | * @param array $mapping |
||
838 | * |
||
839 | * @return array |
||
840 | * @throws MappingException |
||
841 | * @throws \InvalidArgumentException |
||
842 | */ |
||
843 | 1 | private function validateAndCompleteManyToManyMapping(array $mapping) |
|
851 | |||
852 | /** |
||
853 | * @param array $mapping |
||
854 | * |
||
855 | * @throws \InvalidArgumentException |
||
856 | */ |
||
857 | 18 | private function assertMappingOrderBy(array $mapping) |
|
865 | |||
866 | /** |
||
867 | * @param array $mapping |
||
868 | * |
||
869 | * @return array |
||
870 | */ |
||
871 | 17 | private function validateAndCompleteOneToOneMapping(array $mapping) |
|
883 | } |
||
884 |
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.