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 Command 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 Command, and based on these observations, apply Extract Interface, too.
| 1 | <?php namespace Tarsana\Command; |
||
| 17 | class Command { |
||
| 18 | |||
| 19 | protected $name; |
||
| 20 | protected $version; |
||
| 21 | protected $description; |
||
| 22 | |||
| 23 | protected $syntax; |
||
| 24 | protected $descriptions; |
||
| 25 | |||
| 26 | protected $options; |
||
| 27 | protected $args; |
||
| 28 | |||
| 29 | protected $action; |
||
| 30 | protected $commands; |
||
| 31 | |||
| 32 | protected $console; |
||
| 33 | protected $fs; |
||
| 34 | protected $templatesLoader; |
||
| 35 | |||
| 36 | public static function create(callable $action = null) { |
||
| 42 | |||
| 43 | public function __construct() |
||
| 56 | |||
| 57 | /** |
||
| 58 | * name getter and setter. |
||
| 59 | * |
||
| 60 | * @param string |
||
| 61 | * @return mixed |
||
| 62 | */ |
||
| 63 | View Code Duplication | public function name(string $value = null) |
|
| 71 | |||
| 72 | /** |
||
| 73 | * version getter and setter. |
||
| 74 | * |
||
| 75 | * @param string |
||
| 76 | * @return mixed |
||
| 77 | */ |
||
| 78 | public function version(string $value = null) |
||
| 86 | |||
| 87 | /** |
||
| 88 | * description getter and setter. |
||
| 89 | * |
||
| 90 | * @param string |
||
| 91 | * @return mixed |
||
| 92 | */ |
||
| 93 | View Code Duplication | public function description(string $value = null) |
|
| 101 | |||
| 102 | /** |
||
| 103 | * descriptions getter and setter. |
||
| 104 | * |
||
| 105 | * @param string |
||
| 106 | * @return mixed |
||
| 107 | */ |
||
| 108 | public function descriptions(array $value = null) |
||
| 116 | |||
| 117 | /** |
||
| 118 | * syntax getter and setter. |
||
| 119 | * |
||
| 120 | * @param string|null $syntax |
||
| 121 | * @return Syntax|self |
||
| 122 | */ |
||
| 123 | public function syntax(string $syntax = null) |
||
| 131 | |||
| 132 | /** |
||
| 133 | * options getter and setter. |
||
| 134 | * |
||
| 135 | * @param array |
||
| 136 | * @return mixed |
||
| 137 | */ |
||
| 138 | public function options(array $options = null) |
||
| 150 | |||
| 151 | /** |
||
| 152 | * option getter. |
||
| 153 | * |
||
| 154 | * @param string |
||
| 155 | * @return mixed |
||
| 156 | */ |
||
| 157 | public function option(string $name) |
||
| 163 | |||
| 164 | /** |
||
| 165 | * args getter and setter. |
||
| 166 | * |
||
| 167 | * @param stdClass |
||
| 168 | * @return mixed |
||
| 169 | */ |
||
| 170 | public function args(\stdClass $value = null) |
||
| 178 | |||
| 179 | /** |
||
| 180 | * console getter and setter. |
||
| 181 | * |
||
| 182 | * @param ConsoleInterface |
||
| 183 | * @return mixed |
||
| 184 | */ |
||
| 185 | public function console(ConsoleInterface $value = null) |
||
| 196 | |||
| 197 | /** |
||
| 198 | * fs getter and setter. |
||
| 199 | * |
||
| 200 | * @param Tarsana\IO\Filesystem|string |
||
| 201 | * @return mixed |
||
| 202 | */ |
||
| 203 | public function fs($value = null) |
||
| 216 | |||
| 217 | /** |
||
| 218 | * templatesLoader getter and setter. |
||
| 219 | * |
||
| 220 | * @param Tarsana\Command\Interfaces\Template\TemplateLoaderInterface |
||
| 221 | * @return mixed |
||
| 222 | */ |
||
| 223 | public function templatesLoader(TemplateLoaderInterface $value = null) |
||
| 231 | |||
| 232 | public function templatesPath(string $path, string $cachePath = null) { |
||
| 236 | |||
| 237 | public function template(string $name) { |
||
| 242 | |||
| 243 | /** |
||
| 244 | * action getter and setter. |
||
| 245 | * |
||
| 246 | * @param callable |
||
| 247 | * @return mixed |
||
| 248 | */ |
||
| 249 | View Code Duplication | public function action(callable $value = null) |
|
| 257 | |||
| 258 | public function command(string $name, Command $command = null) |
||
| 268 | |||
| 269 | protected function setupSubCommands() |
||
| 274 | |||
| 275 | public function describe(string $name, string $description = null) |
||
| 293 | |||
| 294 | public function run(array $args = null, array $options = [], bool $rawArgs = true) |
||
| 329 | |||
| 330 | public function clear() |
||
| 337 | |||
| 338 | public function parseArguments(array $args) |
||
| 355 | |||
| 356 | protected function handleError(\Exception $e) { |
||
| 360 | |||
| 361 | protected function init() {} |
||
| 363 | |||
| 364 | } |
||
| 365 |
Let’s assume that you have a directory layout like this:
. |-- OtherDir | |-- Bar.php | `-- Foo.php `-- SomeDir `-- Foo.phpand let’s assume the following content of
Bar.php:If both files
OtherDir/Foo.phpandSomeDir/Foo.phpare loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.phpHowever, as
OtherDir/Foo.phpdoes not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: