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 ObjectFactory 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 ObjectFactory, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
70 | class ObjectFactory implements ObjectFactoryInterface |
||
71 | { |
||
72 | /** |
||
73 | * @var SessionInterface |
||
74 | */ |
||
75 | protected $session; |
||
76 | |||
77 | /** |
||
78 | * Initialize the object factory with a session. |
||
79 | * |
||
80 | * @param SessionInterface $session |
||
81 | * @param string[] $parameters |
||
82 | */ |
||
83 | 31 | public function initialize(SessionInterface $session, $parameters = []) |
|
87 | |||
88 | /** |
||
89 | * Convert ACEs to an ACL. |
||
90 | * |
||
91 | * This method does not create a copy of the ACE as the Java OpenCMIS implementation does. # |
||
92 | * For details see the discussion in the mailing list |
||
93 | * |
||
94 | * @see http://mail-archives.apache.org/mod_mbox/chemistry-dev/201501.mbox/<[email protected]> |
||
95 | * |
||
96 | * @param AceInterface[] $aces |
||
97 | * @return AclInterface |
||
98 | */ |
||
99 | 1 | public function convertAces(array $aces) |
|
103 | |||
104 | /** |
||
105 | * @param ObjectDataInterface $objectData |
||
106 | * @return ChangeEventInfoInterface |
||
107 | */ |
||
108 | public function convertChangeEvent(ObjectDataInterface $objectData) |
||
112 | |||
113 | /** |
||
114 | * @param string $changeLogToken |
||
115 | * @param ObjectListInterface $objectList |
||
116 | * @return ChangeEventsInterface |
||
117 | */ |
||
118 | public function convertChangeEvents($changeLogToken, ObjectListInterface $objectList) |
||
122 | |||
123 | /** |
||
124 | * Converts a high level ContentStream object into a low level ContentStream object. |
||
125 | * |
||
126 | * @param StreamInterface $contentStream the original ContentStream object |
||
127 | * @return StreamInterface the ContentStream object |
||
128 | */ |
||
129 | public function convertContentStream(StreamInterface $contentStream) |
||
134 | |||
135 | /** |
||
136 | * Convert given ObjectData to a high level API object |
||
137 | * |
||
138 | * @param ObjectDataInterface $objectData |
||
139 | * @param OperationContextInterface $context |
||
140 | * @return CmisObjectInterface |
||
141 | * @throws CmisRuntimeException |
||
142 | */ |
||
143 | public function convertObject(ObjectDataInterface $objectData, OperationContextInterface $context) |
||
167 | |||
168 | /** |
||
169 | * Converts a list of Policy objects into a list of there string representations |
||
170 | * |
||
171 | * @param PolicyInterface[] $policies |
||
172 | * @return string[] |
||
173 | */ |
||
174 | public function convertPolicies(array $policies) |
||
186 | |||
187 | /** |
||
188 | * Convert Properties in Properties instance to a list of PropertyInterface objects |
||
189 | * |
||
190 | * @param ObjectTypeInterface $objectType |
||
191 | * @param SecondaryTypeInterface[] $secondaryTypes |
||
192 | * @param PropertiesInterface $properties |
||
193 | * @return PropertyInterface[] |
||
194 | * @throws CmisInvalidArgumentException |
||
195 | */ |
||
196 | public function convertPropertiesDataToPropertyList( |
||
218 | |||
219 | /** |
||
220 | * Convert PropertyData into a property API object |
||
221 | * |
||
222 | * @param ObjectTypeInterface $objectType |
||
223 | * @param SecondaryTypeInterface[] $secondaryTypes |
||
224 | * @param PropertyDataInterface $propertyData |
||
225 | * @return PropertyInterface |
||
226 | * @throws CmisRuntimeException |
||
227 | */ |
||
228 | protected function convertProperty( |
||
274 | |||
275 | /** |
||
276 | * Convert properties to their property data objects and put them into a Properties object |
||
277 | * |
||
278 | * @param mixed[] $properties |
||
279 | * @param ObjectTypeInterface|null $type |
||
280 | * @param SecondaryTypeInterface[] $secondaryTypes |
||
281 | * @param Updatability[] $updatabilityFilter |
||
282 | * @return PropertiesInterface |
||
283 | * @throws CmisInvalidArgumentException |
||
284 | */ |
||
285 | 2 | public function convertProperties( |
|
391 | |||
392 | /** |
||
393 | * Get a type definition for the given object type id. If an empty id is given throw an exception. |
||
394 | * |
||
395 | * @param string $objectTypeId |
||
396 | * @return ObjectTypeInterface |
||
397 | * @throws CmisInvalidArgumentException |
||
398 | */ |
||
399 | 1 | private function getTypeDefinition($objectTypeId) |
|
409 | |||
410 | /** |
||
411 | * Get a value from an array. Return <code>null</code> if the key does not exist in the array. |
||
412 | * |
||
413 | * @param integer|string $needle |
||
414 | * @param mixed $haystack |
||
415 | * @return mixed |
||
416 | */ |
||
417 | 1 | private function getValueFromArray($needle, $haystack) |
|
425 | |||
426 | /** |
||
427 | * @param PropertiesInterface $properties |
||
428 | * @return PropertyDataInterface[] |
||
429 | */ |
||
430 | 9 | public function convertQueryProperties(PropertiesInterface $properties) |
|
434 | |||
435 | /** |
||
436 | * Converts ObjectData to QueryResult |
||
437 | * |
||
438 | * @param ObjectDataInterface $objectData |
||
439 | * @return QueryResult |
||
440 | */ |
||
441 | 1 | public function convertQueryResult(ObjectDataInterface $objectData) |
|
445 | |||
446 | /** |
||
447 | * Converts RenditionData to Rendition |
||
448 | * |
||
449 | * @param string $objectId |
||
450 | * @param RenditionDataInterface $renditionData |
||
451 | * @return RenditionInterface |
||
452 | */ |
||
453 | public function convertRendition($objectId, RenditionDataInterface $renditionData) |
||
460 | |||
461 | /** |
||
462 | * @param RepositoryInfoInterface $repositoryInfo |
||
463 | * @return RepositoryInfoInterface |
||
464 | */ |
||
465 | public function convertRepositoryInfo(RepositoryInfoInterface $repositoryInfo) |
||
469 | |||
470 | /** |
||
471 | * Convert a type definition to a type |
||
472 | * |
||
473 | * @param TypeDefinitionInterface $typeDefinition |
||
474 | * @return ObjectTypeInterface |
||
475 | * @throws CmisRuntimeException |
||
476 | */ |
||
477 | 7 | public function convertTypeDefinition(TypeDefinitionInterface $typeDefinition) |
|
498 | |||
499 | /** |
||
500 | * @param string $principal |
||
501 | * @param string[] $permissions |
||
502 | * @return AceInterface |
||
503 | */ |
||
504 | public function createAce($principal, array $permissions) |
||
508 | |||
509 | /** |
||
510 | * @param AceInterface[] $aces |
||
511 | * @return AclInterface |
||
512 | */ |
||
513 | public function createAcl(array $aces) |
||
517 | |||
518 | /** |
||
519 | * Creates an object that implements the ContentStream interface. |
||
520 | * |
||
521 | * @param string $filename |
||
522 | * @param integer $length |
||
523 | * @param string $mimeType |
||
524 | * @param mixed $stream @TODO define datatype |
||
525 | * @param boolean $partial |
||
526 | * @return StreamInterface |
||
527 | */ |
||
528 | public function createContentStream($filename, $length, $mimeType, $stream, $partial = false) |
||
532 | |||
533 | /** |
||
534 | * Create a public API Property that contains the property definition and values. |
||
535 | * |
||
536 | * @param PropertyDefinitionInterface $type |
||
537 | * @param array $values |
||
538 | * @return Property |
||
539 | */ |
||
540 | public function createProperty(PropertyDefinitionInterface $type, array $values) |
||
544 | |||
545 | /** |
||
546 | * Try to determined what object type the given objectData belongs to and return that type. |
||
547 | * |
||
548 | * @param ObjectDataInterface $objectData |
||
549 | * @return ObjectTypeInterface|null The object type or <code>null</code> if type could not be determined |
||
550 | */ |
||
551 | public function getTypeFromObjectData(ObjectDataInterface $objectData) |
||
565 | |||
566 | /** |
||
567 | * Get the bindings object factory for the current session binding |
||
568 | * |
||
569 | * @return BindingsObjectFactoryInterface |
||
570 | */ |
||
571 | 1 | protected function getBindingsObjectFactory() |
|
575 | |||
576 | /** |
||
577 | * Create a type definition with all required properties. |
||
578 | * |
||
579 | * @param string $id This opaque attribute identifies this object-type in the repository. |
||
580 | * @param string $localName This attribute represents the underlying repository’s name for the object-type. |
||
581 | * This field is opaque and has no uniqueness constraint imposed by this specification. |
||
582 | * @param string $baseTypeIdString A value that indicates whether the base type for this object-type is the |
||
583 | * document, folder, relationship, policy, item, or secondary base type. |
||
584 | * @param string $parentId The id of the object-type’s immediate parent type. It MUST be "not set" for a base |
||
585 | * type. Depending on the binding this means it might not exist on the base type object-type definition. |
||
586 | * @param boolean $creatable Indicates whether new objects of this type MAY be created. If the value of this |
||
587 | * attribute is FALSE, the repository MAY contain objects of this type already, but MUST NOT allow new objects |
||
588 | * of this type to be created. |
||
589 | * @param boolean $fileable Indicates whether or not objects of this type are file-able. |
||
590 | * @param boolean $queryable Indicates whether or not this object-type can appear in the FROM clause of a query |
||
591 | * statement. A non-queryable object-type is not visible through the relational view that is used for query, |
||
592 | * and CAN NOT appear in the FROM clause of a query statement. |
||
593 | * @param boolean $controllablePolicy Indicates whether or not objects of this type are controllable via policies. |
||
594 | * Policy objects can only be applied to controllablePolicy objects. |
||
595 | * @param boolean $controllableACL This attribute indicates whether or not objects of this type are controllable by |
||
596 | * ACL’s. Only objects that are controllableACL can have an ACL. |
||
597 | * @param boolean $fulltextIndexed Indicates whether objects of this type are indexed for full-text search for |
||
598 | * querying via the CONTAINS() query predicate. If the value of this attribute is TRUE, the full-text index |
||
599 | * MUST cover the content and MAY cover the metadata. |
||
600 | * @param boolean $includedInSupertypeQuery Indicates whether this type and its subtypes appear in a query of this |
||
601 | * type’s ancestor types. For example: if Invoice is a sub-type of cmis:document, if this is TRUE on Invoice |
||
602 | * then for a query on cmis:document, instances of Invoice will be returned if they match. If this attribute |
||
603 | * is FALSE, no instances of Invoice will be returned even if they match the query. |
||
604 | * @param string $localNamespace This attribute allows repositories to represent the internal namespace of |
||
605 | * the underlying repository’s name for the object-type. |
||
606 | * @param string $queryName Used for query and filter operations on object-types. This is an opaque string with |
||
607 | * limitations. See 2.1.2.1.3 Query Names of the CMIS 1.1 standard for details. |
||
608 | * @param string $displayName Used for presentation by application. |
||
609 | * @param string $description Description of this object-type, such as the nature of content, or its intended use. |
||
610 | * Used for presentation by application. |
||
611 | * @param TypeMutabilityInterface|null $typeMutability |
||
612 | * typeMutability.create - Indicates whether new child types may be created with this type as the parent. |
||
613 | * typeMutability.update - Indicates whether clients may make changes to this type per the constraints |
||
614 | * defined in this specification. |
||
615 | * typeMutability.delete - Indicates whether clients may delete this type if there are no instances of it in |
||
616 | * the repository. |
||
617 | * @return FolderTypeDefinition|DocumentTypeDefinition|RelationshipTypeDefinition|PolicyTypeDefinition|ItemTypeDefinition|SecondaryTypeDefinition |
||
618 | */ |
||
619 | public function createTypeDefinition( |
||
658 | } |
||
659 |
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.