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 Document 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 Document, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
40 | class Document extends DocumentEntity implements ActiveEntityInterface |
||
41 | { |
||
42 | /** |
||
43 | * Indication that save methods must be validated by default, can be altered by calling save |
||
44 | * method with user arguments. |
||
45 | */ |
||
46 | const VALIDATE_SAVE = true; |
||
47 | |||
48 | /** |
||
49 | * Collection name where document should be stored into. |
||
50 | * |
||
51 | * @var string |
||
52 | */ |
||
53 | protected $collection = null; |
||
54 | |||
55 | /** |
||
56 | * Database name/id where document related collection located in. |
||
57 | * |
||
58 | * @var string|null |
||
59 | */ |
||
60 | protected $database = null; |
||
61 | |||
62 | /** |
||
63 | * Set of indexes to be created for associated collection. Use self::INDEX_OPTIONS or "@options" |
||
64 | * for additional parameters. |
||
65 | * |
||
66 | * Example: |
||
67 | * protected $indexes = [ |
||
68 | * ['email' => 1, '@options' => ['unique' => true]], |
||
69 | * ['name' => 1] |
||
70 | * ]; |
||
71 | * |
||
72 | * @link http://php.net/manual/en/mongocollection.ensureindex.php |
||
73 | * @var array |
||
74 | */ |
||
75 | protected $indexes = []; |
||
76 | |||
77 | /** |
||
78 | * @see Component::staticContainer() |
||
79 | * @param array $fields |
||
80 | * @param EntityInterface $parent |
||
81 | * @param ODM $odm |
||
82 | * @param array $odmSchema |
||
83 | */ |
||
84 | public function __construct( |
||
97 | |||
98 | /** |
||
99 | * {@inheritdoc} |
||
100 | * |
||
101 | * @return \MongoId|null |
||
102 | */ |
||
103 | public function primaryKey() |
||
107 | |||
108 | /** |
||
109 | * {@inheritdoc} |
||
110 | */ |
||
111 | public function isLoaded() |
||
115 | |||
116 | /** |
||
117 | * {@inheritdoc} |
||
118 | * |
||
119 | * Create or update document data in database. |
||
120 | * |
||
121 | * @param bool|null $validate Overwrite default option declared in VALIDATE_SAVE to force or |
||
122 | * disable validation before saving. |
||
123 | * @throws DocumentException |
||
124 | * @event saving() |
||
125 | * @event saved() |
||
126 | * @event updating() |
||
127 | * @event updated() |
||
128 | */ |
||
129 | public function save($validate = null) |
||
168 | |||
169 | /** |
||
170 | * {@inheritdoc} |
||
171 | * |
||
172 | * @throws DocumentException |
||
173 | * @event deleting() |
||
174 | * @event deleted() |
||
175 | */ |
||
176 | public function delete() |
||
192 | |||
193 | /** |
||
194 | * {@inheritdoc} See DataEntity class. |
||
195 | * |
||
196 | * ODM: Get instance of Collection or Document associated with described aggregation. |
||
197 | * |
||
198 | * Example: |
||
199 | * $parentGroup = $user->group(); |
||
200 | * echo $user->posts()->where(['published' => true])->count(); |
||
201 | * |
||
202 | * @return mixed|AccessorInterface|DocumentSelector|Document[]|Document |
||
203 | * @throws DocumentException |
||
204 | */ |
||
205 | public function __call($offset, array $arguments) |
||
214 | |||
215 | /** |
||
216 | * Get document aggregation. |
||
217 | * |
||
218 | * @param string $aggregation |
||
219 | * @return DocumentSelector|Document |
||
220 | */ |
||
221 | public function aggregate($aggregation) |
||
242 | |||
243 | /** |
||
244 | * @return Object |
||
245 | */ |
||
246 | public function __debugInfo() |
||
263 | |||
264 | /** |
||
265 | * Instance of ODM Selector associated with specific document. |
||
266 | * |
||
267 | * @see Component::staticContainer() |
||
268 | * @param ODM $odm ODM component, global container will be called if not instance provided. |
||
269 | * @return DocumentSource |
||
270 | * @throws ODMException |
||
271 | */ |
||
272 | View Code Duplication | public static function source(ODM $odm = null) |
|
281 | |||
282 | /** |
||
283 | * Just an alias. |
||
284 | * |
||
285 | * @return DocumentSource |
||
286 | */ |
||
287 | public static function find() |
||
291 | |||
292 | /** |
||
293 | * {@inheritdoc} |
||
294 | * |
||
295 | * Accessor options include field type resolved by DocumentSchema. |
||
296 | * |
||
297 | * @throws ODMException |
||
298 | * @throws DefinitionException |
||
299 | */ |
||
300 | protected function createAccessor($accessor, $value) |
||
315 | |||
316 | /** |
||
317 | * Interpolate aggregation query with document values. |
||
318 | * |
||
319 | * @param array $query |
||
320 | * @return array |
||
321 | */ |
||
322 | protected function interpolateQuery(array $query) |
||
333 | |||
334 | /** |
||
335 | * Get field value using dot notation. |
||
336 | * |
||
337 | * @param string $name |
||
338 | * @return mixed|null |
||
339 | */ |
||
340 | private function dotGet($name) |
||
375 | |||
376 | /** |
||
377 | * Associated mongo collection. |
||
378 | * |
||
379 | * @return \MongoCollection |
||
380 | */ |
||
381 | private function mongoCollection() |
||
385 | } |
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.