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 ObjectRoute 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 ObjectRoute, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 37 | class ObjectRoute extends AbstractModel implements |
||
| 38 | ObjectRouteInterface |
||
| 39 | { |
||
| 40 | /** |
||
| 41 | * A route is active by default. |
||
| 42 | * |
||
| 43 | * @var boolean |
||
| 44 | */ |
||
| 45 | protected $active = true; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * The route's URI. |
||
| 49 | * |
||
| 50 | * @var string |
||
| 51 | */ |
||
| 52 | protected $slug; |
||
| 53 | |||
| 54 | /** |
||
| 55 | * The route's locale. |
||
| 56 | * |
||
| 57 | * @var string |
||
| 58 | */ |
||
| 59 | protected $lang; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * The creation timestamp. |
||
| 63 | * |
||
| 64 | * @var DateTime |
||
| 65 | */ |
||
| 66 | protected $creationDate; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * The last modification timestamp. |
||
| 70 | * |
||
| 71 | * @var DateTime |
||
| 72 | */ |
||
| 73 | protected $lastModificationDate; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * The foreign object type related to this route. |
||
| 77 | * |
||
| 78 | * @var string |
||
| 79 | */ |
||
| 80 | protected $routeObjType; |
||
| 81 | |||
| 82 | /** |
||
| 83 | * The foreign object ID related to this route. |
||
| 84 | * |
||
| 85 | * @var mixed |
||
| 86 | */ |
||
| 87 | protected $routeObjId; |
||
| 88 | |||
| 89 | /** |
||
| 90 | * The foreign object's template identifier. |
||
| 91 | * |
||
| 92 | * @var string |
||
| 93 | */ |
||
| 94 | protected $routeTemplate; |
||
| 95 | |||
| 96 | /** |
||
| 97 | * Retrieve the foreign object's routes options. |
||
| 98 | * |
||
| 99 | * @var array |
||
| 100 | */ |
||
| 101 | protected $routeOptions; |
||
| 102 | |||
| 103 | /** |
||
| 104 | * Retrieve the foreign object's routes options ident. |
||
| 105 | * |
||
| 106 | * @var string |
||
| 107 | */ |
||
| 108 | protected $routeOptionsIdent; |
||
| 109 | |||
| 110 | /** |
||
| 111 | * Store a copy of the original—_preferred_—slug before alterations are made. |
||
| 112 | * |
||
| 113 | * @var string |
||
| 114 | */ |
||
| 115 | private $originalSlug; |
||
| 116 | |||
| 117 | /** |
||
| 118 | * Store the increment used to create a unique slug. |
||
| 119 | * |
||
| 120 | * @var integer |
||
| 121 | */ |
||
| 122 | private $slugInc = 0; |
||
| 123 | |||
| 124 | /** |
||
| 125 | * Store the factory instance for the current class. |
||
| 126 | * |
||
| 127 | * @var FactoryInterface |
||
| 128 | */ |
||
| 129 | private $modelFactory; |
||
| 130 | |||
| 131 | /** |
||
| 132 | * Store the collection loader for the current class. |
||
| 133 | * |
||
| 134 | * @var CollectionLoader |
||
| 135 | */ |
||
| 136 | private $collectionLoader; |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Inject dependencies from a DI Container. |
||
| 140 | * |
||
| 141 | * @param Container $container A dependencies container instance. |
||
| 142 | * @return void |
||
| 143 | */ |
||
| 144 | public function setDependencies(Container $container) |
||
| 149 | |||
| 150 | /** |
||
| 151 | * Event called before _creating_ the object. |
||
| 152 | * |
||
| 153 | * @see Charcoal\Source\StorableTrait::preSave() For the "create" Event. |
||
| 154 | * @return boolean |
||
| 155 | */ |
||
| 156 | public function preSave() |
||
| 164 | |||
| 165 | /** |
||
| 166 | * Event called before _updating_ the object. |
||
| 167 | * |
||
| 168 | * @see Charcoal\Source\StorableTrait::preUpdate() For the "update" Event. |
||
| 169 | * @param array $properties Optional. The list of properties to update. |
||
| 170 | * @return boolean |
||
| 171 | */ |
||
| 172 | public function preUpdate(array $properties = null) |
||
| 179 | |||
| 180 | /** |
||
| 181 | * Determine if the current slug is unique. |
||
| 182 | * |
||
| 183 | * @return boolean |
||
| 184 | */ |
||
| 185 | public function isSlugUnique() |
||
| 219 | |||
| 220 | /** |
||
| 221 | * Generate a unique URL slug for routable object. |
||
| 222 | * |
||
| 223 | * @return self |
||
| 224 | */ |
||
| 225 | public function generateUniqueSlug() |
||
| 239 | |||
| 240 | /** |
||
| 241 | * Set an object model factory. |
||
| 242 | * |
||
| 243 | * @param FactoryInterface $factory The model factory, to create objects. |
||
| 244 | * @return self |
||
| 245 | */ |
||
| 246 | protected function setModelFactory(FactoryInterface $factory) |
||
| 252 | |||
| 253 | /** |
||
| 254 | * Set a model collection loader. |
||
| 255 | * |
||
| 256 | * @param CollectionLoader $loader The collection loader. |
||
| 257 | * @return self |
||
| 258 | */ |
||
| 259 | protected function setCollectionLoader(CollectionLoader $loader) |
||
| 265 | |||
| 266 | /** |
||
| 267 | * Set the object route URI. |
||
| 268 | * |
||
| 269 | * @param string|null $slug The route. |
||
| 270 | * @throws InvalidArgumentException If the slug argument is not a string. |
||
| 271 | * @return self |
||
| 272 | */ |
||
| 273 | View Code Duplication | public function setSlug($slug) |
|
| 289 | |||
| 290 | /** |
||
| 291 | * Set the locale of the object route. |
||
| 292 | * |
||
| 293 | * @param string $lang The route's locale. |
||
| 294 | * @return self |
||
| 295 | */ |
||
| 296 | public function setLang($lang) |
||
| 302 | |||
| 303 | /** |
||
| 304 | * Set the route's last creation date. |
||
| 305 | * |
||
| 306 | * @param string|DateTimeInterface|null $time The date/time value. |
||
| 307 | * @throws InvalidArgumentException If the date/time value is invalid. |
||
| 308 | * @return self |
||
| 309 | */ |
||
| 310 | View Code Duplication | public function setCreationDate($time) |
|
| 339 | |||
| 340 | /** |
||
| 341 | * Set the route's last modification date. |
||
| 342 | * |
||
| 343 | * @param string|DateTimeInterface|null $time The date/time value. |
||
| 344 | * @throws InvalidArgumentException If the date/time value is invalid. |
||
| 345 | * @return self |
||
| 346 | */ |
||
| 347 | View Code Duplication | public function setLastModificationDate($time) |
|
| 376 | |||
| 377 | /** |
||
| 378 | * Set the foreign object type related to this route. |
||
| 379 | * |
||
| 380 | * @param string $type The object type. |
||
| 381 | * @return self |
||
| 382 | */ |
||
| 383 | public function setRouteObjType($type) |
||
| 389 | |||
| 390 | /** |
||
| 391 | * Set the foreign object ID related to this route. |
||
| 392 | * |
||
| 393 | * @param string $id The object ID. |
||
| 394 | * @return self |
||
| 395 | */ |
||
| 396 | public function setRouteObjId($id) |
||
| 402 | |||
| 403 | /** |
||
| 404 | * Set the foreign object's template identifier. |
||
| 405 | * |
||
| 406 | * @param string $template The template identifier. |
||
| 407 | * @return self |
||
| 408 | */ |
||
| 409 | public function setRouteTemplate($template) |
||
| 415 | |||
| 416 | /** |
||
| 417 | * Customize the template's options. |
||
| 418 | * |
||
| 419 | * @param mixed $options Template options. |
||
| 420 | * @return self |
||
| 421 | */ |
||
| 422 | View Code Duplication | public function setRouteOptions($options) |
|
| 432 | |||
| 433 | /** |
||
| 434 | * @param string $routeOptionsIdent Template options ident. |
||
| 435 | * @return self |
||
| 436 | */ |
||
| 437 | public function setRouteOptionsIdent($routeOptionsIdent) |
||
| 443 | |||
| 444 | /** |
||
| 445 | * Retrieve the object model factory. |
||
| 446 | * |
||
| 447 | * @throws RuntimeException If the model factory was not previously set. |
||
| 448 | * @return FactoryInterface |
||
| 449 | */ |
||
| 450 | View Code Duplication | public function modelFactory() |
|
| 451 | { |
||
| 452 | if (!isset($this->modelFactory)) { |
||
| 453 | throw new RuntimeException(sprintf( |
||
| 454 | 'Model Factory is not defined for "%s"', |
||
| 455 | get_class($this) |
||
| 456 | )); |
||
| 457 | } |
||
| 458 | |||
| 459 | return $this->modelFactory; |
||
| 460 | } |
||
| 461 | |||
| 462 | /** |
||
| 463 | * Retrieve the model collection loader. |
||
| 464 | * |
||
| 465 | * @throws RuntimeException If the collection loader was not previously set. |
||
| 466 | * @return CollectionLoader |
||
| 467 | */ |
||
| 468 | public function collectionLoader() |
||
| 479 | |||
| 480 | /** |
||
| 481 | * Retrieve the object route. |
||
| 482 | * |
||
| 483 | * @return string |
||
| 484 | */ |
||
| 485 | public function slug() |
||
| 489 | |||
| 490 | /** |
||
| 491 | * Retrieve the locale of the object route. |
||
| 492 | * |
||
| 493 | * @return string |
||
| 494 | */ |
||
| 495 | public function lang() |
||
| 499 | |||
| 500 | /** |
||
| 501 | * Retrieve the route's creation date. |
||
| 502 | * |
||
| 503 | * @return DateTimeInterface|null |
||
| 504 | */ |
||
| 505 | public function creationDate() |
||
| 509 | |||
| 510 | /** |
||
| 511 | * Retrieve the route's last modification date. |
||
| 512 | * |
||
| 513 | * @return DateTimeInterface|null |
||
| 514 | */ |
||
| 515 | public function lastModificationDate() |
||
| 519 | |||
| 520 | /** |
||
| 521 | * Retrieve the foreign object type related to this route. |
||
| 522 | * |
||
| 523 | * @return string |
||
| 524 | */ |
||
| 525 | public function routeObjType() |
||
| 529 | |||
| 530 | /** |
||
| 531 | * Retrieve the foreign object ID related to this route. |
||
| 532 | * |
||
| 533 | * @return string |
||
| 534 | */ |
||
| 535 | public function routeObjId() |
||
| 539 | |||
| 540 | /** |
||
| 541 | * Retrieve the foreign object's template identifier. |
||
| 542 | * |
||
| 543 | * @return string |
||
| 544 | */ |
||
| 545 | public function routeTemplate() |
||
| 549 | |||
| 550 | /** |
||
| 551 | * Retrieve the template's customized options. |
||
| 552 | * |
||
| 553 | * @return array |
||
| 554 | */ |
||
| 555 | public function routeOptions() |
||
| 559 | |||
| 560 | /** |
||
| 561 | * @return string |
||
| 562 | */ |
||
| 563 | public function routeOptionsIdent() |
||
| 567 | |||
| 568 | /** |
||
| 569 | * Alias of {@see self::slug()}. |
||
| 570 | * |
||
| 571 | * @return string |
||
| 572 | */ |
||
| 573 | public function __toString() |
||
| 577 | } |
||
| 578 |
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.