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 ResourceType 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 ResourceType, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
29 | abstract class ResourceType |
||
30 | { |
||
31 | /** |
||
32 | * Name of the resource described by this class instance. |
||
33 | * |
||
34 | * @var string |
||
35 | */ |
||
36 | private $name; |
||
37 | |||
38 | /** |
||
39 | * Namespace name in which resource described by this class instance |
||
40 | * belongs to. |
||
41 | * |
||
42 | * @var string |
||
43 | */ |
||
44 | private $namespaceName; |
||
45 | |||
46 | /** |
||
47 | * The fully qualified name of the resource described by this class instance. |
||
48 | * |
||
49 | * @var string |
||
50 | */ |
||
51 | private $fullName; |
||
52 | |||
53 | /** |
||
54 | * The type the resource described by this class instance. |
||
55 | * Note: either Entity or Complex Type. |
||
56 | * |
||
57 | * @var ResourceTypeKind |
||
58 | */ |
||
59 | protected $resourceTypeKind; |
||
60 | |||
61 | /** |
||
62 | * @var bool |
||
63 | */ |
||
64 | private $abstractType; |
||
65 | |||
66 | /** |
||
67 | * Refrence to ResourceType instance for base type, if any. |
||
68 | * |
||
69 | * @var ResourceType |
||
70 | */ |
||
71 | private $baseType; |
||
72 | |||
73 | /** |
||
74 | * Collection of ResourceProperty for all properties declared on the |
||
75 | * resource described by this class instance (This does not include |
||
76 | * base type properties). |
||
77 | * |
||
78 | * @var ResourceProperty[] indexed by name |
||
79 | */ |
||
80 | private $propertiesDeclaredOnThisType = []; |
||
81 | |||
82 | /** |
||
83 | * Collection of ResourceStreamInfo for all named streams declared on |
||
84 | * the resource described by this class instance (This does not include |
||
85 | * base type properties). |
||
86 | * |
||
87 | * @var ResourceStreamInfo[] indexed by name |
||
88 | */ |
||
89 | private $namedStreamsDeclaredOnThisType = []; |
||
90 | |||
91 | /** |
||
92 | * Collection of ResourceProperty for all properties declared on this type. |
||
93 | * and base types. |
||
94 | * |
||
95 | * @var ResourceProperty[] indexed by name |
||
96 | */ |
||
97 | private $allProperties = []; |
||
98 | |||
99 | /** |
||
100 | * Collection of ResourceStreamInfo for all named streams declared on this type. |
||
101 | * and base types. |
||
102 | * |
||
103 | * @var ResourceStreamInfo[] |
||
104 | */ |
||
105 | private $allNamedStreams = []; |
||
106 | |||
107 | /** |
||
108 | * Collection of properties which has etag defined subset of $_allProperties. |
||
109 | * |
||
110 | * @var ResourceProperty[] |
||
111 | */ |
||
112 | private $eTagProperties = []; |
||
113 | |||
114 | /** |
||
115 | * Collection of key properties subset of $_allProperties. |
||
116 | * |
||
117 | * @var ResourceProperty[] |
||
118 | */ |
||
119 | private $keyProperties = []; |
||
120 | |||
121 | /** |
||
122 | * Whether the resource type described by this class instance is a MLE or not. |
||
123 | * |
||
124 | * @var bool |
||
125 | */ |
||
126 | private $isMediaLinkEntry = false; |
||
127 | |||
128 | /** |
||
129 | * Whether the resource type described by this class instance has bag properties |
||
130 | * Note: This has been initialized with null, later in hasBagProperty method, |
||
131 | * this flag will be set to boolean value. |
||
132 | * |
||
133 | * @var bool |
||
134 | */ |
||
135 | private $hasBagProperty = null; |
||
136 | |||
137 | /** |
||
138 | * Whether the resource type described by this class instance has named streams |
||
139 | * Note: This has been intitialized with null, later in hasNamedStreams method, |
||
140 | * this flag will be set to boolean value. |
||
141 | * |
||
142 | * @var bool |
||
143 | */ |
||
144 | private $hasNamedStreams = null; |
||
145 | |||
146 | /** |
||
147 | * ReflectionClass (for complex/Entity) or IType (for Primitive) instance for |
||
148 | * the resource (type) described by this class instance. |
||
149 | * |
||
150 | * @var \ReflectionClass|IType|string |
||
151 | */ |
||
152 | private $type; |
||
153 | |||
154 | /** |
||
155 | * To store any custom information related to this class instance. |
||
156 | * |
||
157 | * @var object |
||
158 | */ |
||
159 | private $customState; |
||
160 | |||
161 | /** |
||
162 | * Array to detect looping in bag's complex type. |
||
163 | * |
||
164 | * @var array |
||
165 | */ |
||
166 | private $arrayToDetectLoopInComplexBag; |
||
167 | |||
168 | /** |
||
169 | * Create new instance of ResourceType. |
||
170 | * |
||
171 | * @param \ReflectionClass|IType $instanceType Instance type for the resource, |
||
172 | * for entity and |
||
173 | * complex this will |
||
174 | * be 'ReflectionClass' and for |
||
175 | * primitive type this |
||
176 | * will be IType |
||
177 | * @param ResourceTypeKind $resourceTypeKind Kind of resource (Entity, Complex or Primitive) |
||
178 | * @param string $name Name of the resource |
||
179 | * @param string $namespaceName Namespace of the resource |
||
180 | * @param ResourceType $baseType Base type of the resource, if exists |
||
181 | * @param bool $isAbstract Whether resource is abstract |
||
182 | * |
||
183 | * @throws \InvalidArgumentException |
||
184 | */ |
||
185 | protected function __construct( |
||
205 | |||
206 | /** |
||
207 | * Get reference to ResourceType for base class. |
||
208 | * |
||
209 | * @return ResourceType |
||
210 | */ |
||
211 | public function getBaseType() |
||
215 | |||
216 | /** |
||
217 | * To check whether this resource type has base type. |
||
218 | * |
||
219 | * @return bool True if base type is defined, false otherwise |
||
220 | */ |
||
221 | public function hasBaseType() |
||
225 | |||
226 | /** |
||
227 | * To get custom state object for this type. |
||
228 | * |
||
229 | * @return object |
||
230 | */ |
||
231 | public function getCustomState() |
||
235 | |||
236 | /** |
||
237 | * To set custom state object for this type. |
||
238 | * |
||
239 | * @param ResourceSet $object The custom object |
||
240 | */ |
||
241 | public function setCustomState($object) |
||
245 | |||
246 | /** |
||
247 | * Get the instance type. If the resource type describes a complex or entity type, |
||
248 | * then this function returns reference to ReflectionClass instance for the type. |
||
249 | * If resource type describes a primitive type, then this function returns ITYpe. |
||
250 | * |
||
251 | * @return \ReflectionClass|IType |
||
252 | */ |
||
253 | public function getInstanceType() |
||
261 | |||
262 | /** |
||
263 | * Get name of the type described by this resource type. |
||
264 | * |
||
265 | * @return string |
||
266 | */ |
||
267 | public function getName() |
||
271 | |||
272 | /** |
||
273 | * Get the namespace under which the type described by this resource type is |
||
274 | * defined. |
||
275 | * |
||
276 | * @return string |
||
277 | */ |
||
278 | public function getNamespace() |
||
282 | |||
283 | /** |
||
284 | * Get full name (namespacename.typename) of the type described by this resource |
||
285 | * type. |
||
286 | * |
||
287 | * @return string |
||
288 | */ |
||
289 | public function getFullName() |
||
293 | |||
294 | /** |
||
295 | * To check whether the type described by this resource type is abstract or not. |
||
296 | * |
||
297 | * @return bool True if type is abstract else False |
||
298 | */ |
||
299 | public function isAbstract() |
||
303 | |||
304 | /** |
||
305 | * To get the kind of type described by this resource class. |
||
306 | * |
||
307 | * @return ResourceTypeKind |
||
308 | */ |
||
309 | public function getResourceTypeKind() |
||
313 | |||
314 | /** |
||
315 | * To check whether the type described by this resource type is MLE. |
||
316 | * |
||
317 | * @return bool True if type is MLE else False |
||
318 | */ |
||
319 | public function isMediaLinkEntry() |
||
323 | |||
324 | /** |
||
325 | * Set the resource type as MLE or non-MLE. |
||
326 | * |
||
327 | * @param bool $isMLE True to set as MLE, false for non-MLE |
||
328 | */ |
||
329 | public function setMediaLinkEntry($isMLE) |
||
339 | |||
340 | /** |
||
341 | * Add a property belongs to this resource type instance. |
||
342 | * |
||
343 | * @param ResourceProperty $property Property to add |
||
344 | * @param bool $throw Throw exception on name collision? |
||
345 | * |
||
346 | * @throws InvalidOperationException |
||
347 | */ |
||
348 | public function addProperty(ResourceProperty $property, $throw = true) |
||
403 | |||
404 | /** |
||
405 | * Get collection properties belongs to this resource type (excluding base class |
||
406 | * properties). This function returns empty array in case of resource type |
||
407 | * for primitive types. |
||
408 | * |
||
409 | * @return ResourceProperty[] |
||
410 | */ |
||
411 | public function getPropertiesDeclaredOnThisType() |
||
415 | |||
416 | /** |
||
417 | * Get collection properties belongs to this resource type including base class |
||
418 | * properties. This function returns empty array in case of resource type |
||
419 | * for primitive types. |
||
420 | * |
||
421 | * @return ResourceProperty[] |
||
422 | */ |
||
423 | public function getAllProperties() |
||
437 | |||
438 | /** |
||
439 | * Get collection key properties belongs to this resource type. This |
||
440 | * function returns non-empty array only for resource type representing |
||
441 | * an entity type. |
||
442 | * |
||
443 | * @return ResourceProperty[] |
||
444 | */ |
||
445 | public function getKeyProperties() |
||
462 | |||
463 | /** |
||
464 | * Get collection of e-tag properties belongs to this type. |
||
465 | * |
||
466 | * @return ResourceProperty[] |
||
467 | */ |
||
468 | public function getETagProperties() |
||
480 | |||
481 | /** |
||
482 | * To check this type has any eTag properties. |
||
483 | * |
||
484 | * @return bool |
||
485 | */ |
||
486 | public function hasETagProperties() |
||
492 | |||
493 | /** |
||
494 | * Try to get ResourceProperty for a property defined for this resource type |
||
495 | * excluding base class properties. |
||
496 | * |
||
497 | * @param string $propertyName The name of the property to resolve |
||
498 | * |
||
499 | * @return ResourceProperty|null |
||
500 | */ |
||
501 | public function resolvePropertyDeclaredOnThisType($propertyName) |
||
508 | |||
509 | /** |
||
510 | * Try to get ResourceProperty for a property defined for this resource type |
||
511 | * including base class properties. |
||
512 | * |
||
513 | * @param string $propertyName The name of the property to resolve |
||
514 | * |
||
515 | * @return ResourceProperty|null |
||
516 | */ |
||
517 | public function resolveProperty($propertyName) |
||
524 | |||
525 | /** |
||
526 | * Add a named stream belongs to this resource type instance. |
||
527 | * |
||
528 | * @param ResourceStreamInfo $namedStream ResourceStreamInfo instance describing the named stream to add |
||
529 | * |
||
530 | * @throws InvalidOperationException |
||
531 | */ |
||
532 | public function addNamedStream(ResourceStreamInfo $namedStream) |
||
561 | |||
562 | /** |
||
563 | * Get collection of ResourceStreamInfo describing the named streams belongs |
||
564 | * to this resource type (excluding base class properties). |
||
565 | * |
||
566 | * @return ResourceStreamInfo[] |
||
567 | */ |
||
568 | public function getNamedStreamsDeclaredOnThisType() |
||
572 | |||
573 | /** |
||
574 | * Get collection of ResourceStreamInfo describing the named streams belongs |
||
575 | * to this resource type including base class named streams. |
||
576 | * |
||
577 | * @return ResourceStreamInfo[] |
||
578 | */ |
||
579 | public function getAllNamedStreams() |
||
595 | |||
596 | /** |
||
597 | * Try to get ResourceStreamInfo for a named stream defined for this |
||
598 | * resource type excluding base class named streams. |
||
599 | * |
||
600 | * @param string $namedStreamName Name of the named stream to resolve |
||
601 | * |
||
602 | * @return ResourceStreamInfo|null |
||
603 | */ |
||
604 | public function tryResolveNamedStreamDeclaredOnThisTypeByName($namedStreamName) |
||
611 | |||
612 | /** |
||
613 | * Try to get ResourceStreamInfo for a named stream defined for this resource |
||
614 | * type including base class named streams. |
||
615 | * |
||
616 | * @param string $namedStreamName Name of the named stream to resolve |
||
617 | * |
||
618 | * @return ResourceStreamInfo|null |
||
619 | */ |
||
620 | public function tryResolveNamedStreamByName($namedStreamName) |
||
627 | |||
628 | /** |
||
629 | * Check this resource type instance has named stream associated with it |
||
630 | * Note: This is an internal method used by library. Devs don't use this. |
||
631 | * |
||
632 | * @return bool true if resource type instance has named stream else false |
||
633 | */ |
||
634 | public function hasNamedStream() |
||
648 | |||
649 | /** |
||
650 | * Check this resource type instance has bag property associated with it |
||
651 | * Note: This is an internal method used by library. Devs don't use this. |
||
652 | * |
||
653 | * @param array &$arrayToDetectLoopInComplexType array for detecting loop |
||
654 | * |
||
655 | * @return bool|null true if resource type instance has bag property else false |
||
656 | */ |
||
657 | public function hasBagProperty(&$arrayToDetectLoopInComplexType) |
||
731 | |||
732 | /** |
||
733 | * Validate the type. |
||
734 | * |
||
735 | * |
||
736 | * @throws InvalidOperationException |
||
737 | */ |
||
738 | public function validateType() |
||
749 | |||
750 | /** |
||
751 | * To check the type described by this resource type is assignable from |
||
752 | * a type described by another resource type. Or this type is a sub-type |
||
753 | * of (derived from the) given resource type. |
||
754 | * |
||
755 | * @param ResourceType $resourceType Another resource type |
||
756 | * |
||
757 | * @return bool |
||
758 | */ |
||
759 | public function isAssignableFrom(ResourceType $resourceType) |
||
772 | |||
773 | /** |
||
774 | * Get predefined ResourceType for a primitive type. |
||
775 | * |
||
776 | * @param EdmPrimitiveType $typeCode Typecode of primitive type |
||
777 | * |
||
778 | * @throws InvalidArgumentException |
||
779 | * |
||
780 | * @return ResourceType |
||
781 | */ |
||
782 | public static function getPrimitiveResourceType($typeCode) |
||
820 | |||
821 | /** |
||
822 | * @param string $property |
||
823 | */ |
||
824 | public function setPropertyValue($entity, $property, $value) |
||
830 | |||
831 | public function getPropertyValue($entity, $property) |
||
835 | |||
836 | public function __sleep() |
||
846 | |||
847 | public function __wakeup() |
||
858 | } |
||
859 |
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.