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 Database 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 Database, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | class Database |
||
17 | { |
||
18 | /** |
||
19 | * |
||
20 | * @var \Sokil\Mongo\Client |
||
21 | */ |
||
22 | private $client; |
||
23 | |||
24 | /** |
||
25 | * @var \MongoDB |
||
26 | */ |
||
27 | private $database; |
||
28 | |||
29 | /** |
||
30 | * @var string |
||
31 | */ |
||
32 | private $databaseName; |
||
33 | |||
34 | /** |
||
35 | * @var array map collection name to class |
||
36 | */ |
||
37 | private $mapping = array(); |
||
38 | |||
39 | /** |
||
40 | * @var array map regexp pattern of collection name to class |
||
41 | */ |
||
42 | private $regexpMapping = array(); |
||
43 | |||
44 | /** |
||
45 | * @var array pool of initialised collections |
||
46 | */ |
||
47 | private $collectionPool = array(); |
||
48 | |||
49 | /** |
||
50 | * |
||
51 | * @var bool is collection pool enabled |
||
52 | */ |
||
53 | private $collectionPoolEnabled = true; |
||
54 | |||
55 | /** |
||
56 | * @param Client $client |
||
57 | * @param \MongoDB|string $database |
||
58 | */ |
||
59 | public function __construct(Client $client, $database) |
||
70 | |||
71 | /** |
||
72 | * |
||
73 | * @param string $username |
||
74 | * @param string $password |
||
75 | */ |
||
76 | public function authenticate($username, $password) |
||
80 | |||
81 | public function logout() |
||
87 | |||
88 | public function __get($name) |
||
92 | |||
93 | /** |
||
94 | * @return string get name of database |
||
95 | */ |
||
96 | public function getName() |
||
100 | |||
101 | /** |
||
102 | * |
||
103 | * @return \MongoDB |
||
104 | */ |
||
105 | public function getMongoDB() |
||
116 | |||
117 | /** |
||
118 | * |
||
119 | * @return \Sokil\Mongo\Client |
||
120 | */ |
||
121 | public function getClient() |
||
125 | |||
126 | public function disableCollectionPool() |
||
131 | |||
132 | public function enableCollectionPool() |
||
137 | |||
138 | public function isCollectionPoolEnabled() |
||
142 | |||
143 | public function clearCollectionPool() |
||
148 | |||
149 | public function isCollectionPoolEmpty() |
||
153 | |||
154 | /** |
||
155 | * Reset specified mapping |
||
156 | * |
||
157 | * @return \Sokil\Mongo\Client |
||
158 | */ |
||
159 | public function resetMapping() |
||
165 | |||
166 | /** |
||
167 | * Map collection name to class |
||
168 | * |
||
169 | * @param string|array $name collection name or array like |
||
170 | * [collectionName => collectionClass, ...] |
||
171 | * @param string|array|Definition|null $classDefinition if $name is string, then full class name or array |
||
172 | * with parameters, else omitted |
||
173 | * @return \Sokil\Mongo\Client |
||
174 | */ |
||
175 | public function map($name, $classDefinition = null) |
||
198 | |||
199 | /** |
||
200 | * Define collection through array or Definition instance |
||
201 | * |
||
202 | * @param string $name collection name |
||
203 | * @param Definition|array|string $definition collection definition |
||
204 | * @return Database |
||
205 | */ |
||
206 | private function defineCollection($name, $definition) |
||
228 | |||
229 | /** |
||
230 | * Get class name mapped to collection |
||
231 | * |
||
232 | * @param string $name name of collection |
||
233 | * @param array $defaultDefinition definition used when no definition found for defined class |
||
234 | * @throws Exception |
||
235 | * @return string|array name of class or array of class definition |
||
236 | */ |
||
237 | private function getCollectionDefinition($name, array $defaultDefinition = null) |
||
276 | |||
277 | /** |
||
278 | * Create collection |
||
279 | * |
||
280 | * @param string $name name of collection |
||
281 | * @param array|null $options array of options |
||
282 | * @return \Sokil\Mongo\Collection |
||
283 | * @throws \Sokil\Mongo\Exception |
||
284 | */ |
||
285 | public function createCollection($name, array $options = null) |
||
306 | |||
307 | /** |
||
308 | * |
||
309 | * @param string $name name of collection |
||
310 | * @param int $maxElements The maximum number of elements to store in the collection. |
||
311 | * @param int $size Size in bytes. |
||
312 | * @return \Sokil\Mongo\Collection |
||
313 | * @throws Exception |
||
314 | */ |
||
315 | public function createCappedCollection($name, $maxElements, $size) |
||
329 | |||
330 | /** |
||
331 | * |
||
332 | * @param string $name name of collection |
||
333 | * @return \Sokil\Mongo\Collection |
||
334 | * @throws \Sokil\Mongo\Exception |
||
335 | */ |
||
336 | View Code Duplication | public function getCollection($name) |
|
361 | |||
362 | /** |
||
363 | * Get Document instance by it's reference |
||
364 | * |
||
365 | * @param array $ref reference to document |
||
366 | * @param bool $useDocumentPool try to get document from pool or fetch document from database |
||
367 | * |
||
368 | * @return Document|null |
||
369 | */ |
||
370 | View Code Duplication | public function getDocumentByReference(array $ref, $useDocumentPool = true) |
|
379 | |||
380 | /** |
||
381 | * Get instance of GridFS |
||
382 | * |
||
383 | * @param string $name prefix of files and chunks collection |
||
384 | * @return \Sokil\Mongo\GridFS |
||
385 | * @throws \Sokil\Mongo\Exception |
||
386 | */ |
||
387 | View Code Duplication | public function getGridFS($name = 'fs') |
|
412 | |||
413 | /** |
||
414 | * |
||
415 | * @param string $channel name of channel |
||
416 | * @return \Sokil\Mongo\Queue |
||
417 | */ |
||
418 | public function getQueue($channel) |
||
422 | |||
423 | /** |
||
424 | * Get cache |
||
425 | * |
||
426 | * @param string $namespace name of collection to be created in database |
||
427 | * |
||
428 | * @return Cache |
||
429 | */ |
||
430 | public function getCache($namespace) |
||
434 | |||
435 | public function readPrimaryOnly() |
||
440 | |||
441 | public function readPrimaryPreferred(array $tags = null) |
||
446 | |||
447 | public function readSecondaryOnly(array $tags = null) |
||
452 | |||
453 | public function readSecondaryPreferred(array $tags = null) |
||
458 | |||
459 | public function readNearest(array $tags = null) |
||
464 | |||
465 | public function getReadPreference() |
||
469 | |||
470 | /** |
||
471 | * Define write concern. |
||
472 | * May be used only if mongo extension version >=1.5 |
||
473 | * |
||
474 | * @param string|integer $w write concern |
||
475 | * @param int $timeout timeout in milliseconds |
||
476 | * @return \Sokil\Mongo\Database |
||
477 | * @throws \Sokil\Mongo\Exception |
||
478 | */ |
||
479 | public function setWriteConcern($w, $timeout = 10000) |
||
487 | |||
488 | /** |
||
489 | * Define unacknowledged write concern. |
||
490 | * May be used only if mongo extension version >=1.5 |
||
491 | * |
||
492 | * @param int $timeout timeout in milliseconds |
||
493 | * @return \Sokil\Mongo\Database |
||
494 | */ |
||
495 | public function setUnacknowledgedWriteConcern($timeout = 10000) |
||
500 | |||
501 | /** |
||
502 | * Define majority write concern. |
||
503 | * May be used only if mongo extension version >=1.5 |
||
504 | * |
||
505 | * @param int $timeout timeout in milliseconds |
||
506 | * @return \Sokil\Mongo\Database |
||
507 | */ |
||
508 | public function setMajorityWriteConcern($timeout = 10000) |
||
513 | |||
514 | /** |
||
515 | * Get current write concern |
||
516 | * May be used only if mongo extension version >=1.5 |
||
517 | * |
||
518 | * @return mixed |
||
519 | */ |
||
520 | public function getWriteConcern() |
||
524 | |||
525 | /** |
||
526 | * Execute Mongo command |
||
527 | * |
||
528 | * @param array $command |
||
529 | * @param array $options |
||
530 | * @return array |
||
531 | */ |
||
532 | public function executeCommand(array $command, array $options = array()) |
||
536 | |||
537 | public function executeJS($code, array $args = array()) |
||
546 | |||
547 | public function stats() |
||
553 | |||
554 | public function getLastError() |
||
558 | |||
559 | public function getProfilerParams() |
||
565 | |||
566 | public function getProfilerLevel() |
||
571 | |||
572 | public function getProfilerSlowMs() |
||
577 | |||
578 | public function disableProfiler() |
||
584 | |||
585 | public function profileSlowQueries($slowms = 100) |
||
592 | |||
593 | public function profileAllQueries($slowms = null) |
||
605 | |||
606 | /** |
||
607 | * |
||
608 | * @return \Sokil\Mongo\Cursor |
||
609 | */ |
||
610 | public function findProfilerRows() |
||
617 | } |
||
618 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.