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 Filesystem 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 Filesystem, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
38 | class Filesystem |
||
39 | { |
||
40 | /** |
||
41 | * @var Console |
||
42 | */ |
||
43 | protected $console; |
||
44 | |||
45 | /** |
||
46 | * Construct Filesystem |
||
47 | * |
||
48 | * @param Console $console The console |
||
49 | */ |
||
50 | public function __construct(Console $console) |
||
54 | |||
55 | |||
56 | /** |
||
57 | * Remove a file |
||
58 | * |
||
59 | * @param string $file The file or dir |
||
60 | * |
||
61 | * @return bool |
||
62 | */ |
||
63 | public function remove($file) |
||
75 | |||
76 | /** |
||
77 | * Checks if a directory is empty |
||
78 | * |
||
79 | * @param string $dir The dir |
||
80 | * |
||
81 | * @return bool |
||
82 | */ |
||
83 | public function isDirEmpty($dir) |
||
89 | |||
90 | /** |
||
91 | * Recursively remove a directory |
||
92 | * |
||
93 | * Uses the process component if proc_open is enabled on the PHP |
||
94 | * installation. |
||
95 | * |
||
96 | * @param string $directory The dir |
||
97 | * |
||
98 | * @return bool |
||
99 | */ |
||
100 | public function removeDirectory($directory) |
||
124 | |||
125 | /** |
||
126 | * Recursively delete directory using PHP iterators. |
||
127 | * |
||
128 | * Uses a CHILD_FIRST RecursiveIteratorIterator to sort files |
||
129 | * before directories, creating a single non-recursive loop |
||
130 | * to delete files/directories in the correct order. |
||
131 | * |
||
132 | * @param string $directory The dir |
||
133 | * |
||
134 | * @return bool |
||
135 | */ |
||
136 | public function removeDirectoryPhp($directory) |
||
151 | |||
152 | /** |
||
153 | * Ensures a dir exists |
||
154 | * |
||
155 | * @param string $directory The dir |
||
156 | * |
||
157 | * @return void |
||
158 | */ |
||
159 | public function ensureDirectoryExists($directory) |
||
174 | |||
175 | /** |
||
176 | * Copy then delete is a non-atomic version of {@link rename}. |
||
177 | * |
||
178 | * Some systems can't rename and also don't have proc_open, |
||
179 | * which requires this solution. |
||
180 | * |
||
181 | * @param string $source The source |
||
182 | * @param string $target The target |
||
183 | * |
||
184 | * @return void |
||
185 | */ |
||
186 | public function copyThenRemove($source, $target) |
||
191 | |||
192 | /** |
||
193 | * Copy a file or folder to a target destination |
||
194 | * |
||
195 | * @param string $source The source |
||
196 | * @param string $target The target |
||
197 | * |
||
198 | * @return void |
||
199 | */ |
||
200 | public function copy($source, $target) |
||
222 | |||
223 | /** |
||
224 | * Rename a file or folder to a target destination |
||
225 | * |
||
226 | * @param string $source The source |
||
227 | * @param string $target The target |
||
228 | * |
||
229 | * @return void |
||
230 | */ |
||
231 | public function rename($source, $target) |
||
270 | |||
271 | /** |
||
272 | * Returns the shortest path from $from to $to |
||
273 | * |
||
274 | * @param string $from From |
||
275 | * @param string $to To |
||
276 | * @param bool $directories if true, the source/target are considered to be directories |
||
277 | * |
||
278 | * @throws \InvalidArgumentException |
||
279 | * |
||
280 | * @return string |
||
281 | */ |
||
282 | public function findShortestPath($from, $to, $directories = false) |
||
314 | |||
315 | /** |
||
316 | * Returns PHP code that, when executed in $from, will return the path to $to |
||
317 | * |
||
318 | * @param string $from From |
||
319 | * @param string $to To |
||
320 | * @param bool $directories if true, the source/target are considered to be directories |
||
321 | * |
||
322 | * @throws \InvalidArgumentException |
||
323 | * |
||
324 | * @return string |
||
325 | */ |
||
326 | public function findShortestPathCode($from, $to, $directories = false) |
||
358 | |||
359 | /** |
||
360 | * Checks if the given path is absolute |
||
361 | * |
||
362 | * @param string $path Path |
||
363 | * |
||
364 | * @return bool |
||
365 | */ |
||
366 | public function isAbsolutePath($path) |
||
370 | |||
371 | /** |
||
372 | * Returns size of a file or directory specified by path. If a directory is |
||
373 | * given, it's size will be computed recursively. |
||
374 | * |
||
375 | * @param string $path Path to the file or directory |
||
376 | * |
||
377 | * @throws \RuntimeException |
||
378 | * |
||
379 | * @return int |
||
380 | */ |
||
381 | public function size($path) |
||
392 | |||
393 | /** |
||
394 | * Normalize a path. This replaces backslashes with slashes, removes ending |
||
395 | * slash and collapses redundant separators and up-level references. |
||
396 | * |
||
397 | * @param string $path Path to the file or directory |
||
398 | * |
||
399 | * @return string |
||
400 | */ |
||
401 | public function normalizePath($path) |
||
431 | |||
432 | /** |
||
433 | * Get directory size |
||
434 | * |
||
435 | * @param string $directory The dir |
||
436 | * |
||
437 | * @return int |
||
438 | */ |
||
439 | protected function directorySize($directory) |
||
453 | } |
||
454 | ?> |
||
455 |
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.