Complex classes like ClassManager 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 ClassManager, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class ClassManager implements RenderableInterface, DumpableInterface, StructureWithMethodsInterface, MultilineCommentableInterface |
||
24 | { |
||
25 | |||
26 | use MultilineCommentTrait; |
||
27 | use TemplateTrait; |
||
28 | |||
29 | /** |
||
30 | * Collection of MethodManager |
||
31 | * |
||
32 | * @var ArrayCollection |
||
33 | * @Assert\NotNull(message="Properties Collection can not be empty!") |
||
34 | * @Assert\Valid() |
||
35 | */ |
||
36 | private $methods = null; |
||
37 | |||
38 | /** |
||
39 | * Collection of PropertyManager |
||
40 | * |
||
41 | * @Type("Doctrine\Common\Collections\ArrayCollection<SimpleEntityGeneratorBundle\Lib\Items\PropertyManager>") |
||
42 | * @Assert\NotNull(message="Properties Collection can not be empty!") |
||
43 | * @Assert\Valid() |
||
44 | */ |
||
45 | private $properties = null; |
||
46 | |||
47 | /** |
||
48 | * Interface namespace |
||
49 | * |
||
50 | * @var InterfaceManager |
||
51 | * @Assert\Valid() |
||
52 | */ |
||
53 | private $interface = null; |
||
54 | |||
55 | /** |
||
56 | * Class constructor |
||
57 | * |
||
58 | * @var ClassConstructorManager |
||
59 | * @Assert\NotNull(message="Constructor can not be null!") |
||
60 | * @Assert\Valid() |
||
61 | */ |
||
62 | private $constructor = null; |
||
63 | |||
64 | /** |
||
65 | * Test Class |
||
66 | * |
||
67 | * @var TestClassManager |
||
68 | * @Assert\Valid() |
||
69 | * @var TestClassManager |
||
70 | */ |
||
71 | private $testClass = null; |
||
72 | |||
73 | /** |
||
74 | * namespace of class - class name is retrieved from namespace |
||
75 | * |
||
76 | * @Type("string") |
||
77 | * @Assert\NotBlank(message="Namespace can not be blank!") |
||
78 | */ |
||
79 | private $namespace = ""; |
||
80 | |||
81 | /** |
||
82 | * Comment under class |
||
83 | * |
||
84 | * @Type("string") |
||
85 | */ |
||
86 | private $comment = ""; |
||
87 | |||
88 | /** |
||
89 | * Base class namespace |
||
90 | * |
||
91 | * @Type("string") |
||
92 | */ |
||
93 | private $extends = ""; |
||
94 | |||
95 | /** |
||
96 | * @SerializedName("class_manager_template_path") |
||
97 | * @Type("string") |
||
98 | * @Assert\Type("string") |
||
99 | */ |
||
100 | private $classManagerTemplatePath = ""; |
||
101 | |||
102 | /** |
||
103 | * |
||
104 | * @SerializedName("class_constructor_manager_template_path") |
||
105 | * @Type("string") |
||
106 | * @Assert\Type("string") |
||
107 | */ |
||
108 | private $classConstructorManagerTemplatePath = ""; |
||
109 | |||
110 | /** |
||
111 | * @SerializedName("interface_manager_template_path") |
||
112 | * @Type("string") |
||
113 | * @Assert\Type("string") |
||
114 | */ |
||
115 | private $interfaceManagerTemplatePath = ""; |
||
116 | |||
117 | /** |
||
118 | * @SerializedName("test_class_manager_template_path") |
||
119 | * @Type("string") |
||
120 | * @Assert\Type("string") |
||
121 | */ |
||
122 | private $testClassManagerTemplatePath = ""; |
||
123 | |||
124 | /** |
||
125 | * @Type("SimpleEntityGeneratorBundle\Lib\ClassConfig") |
||
126 | * @SerializedName("configuration") |
||
127 | * @Assert\Valid() |
||
128 | * @var \SimpleEntityGeneratorBundle\Lib\ClassConfig |
||
129 | */ |
||
130 | private $configuration; |
||
131 | |||
132 | /** |
||
133 | * @SerializedName("implements") |
||
134 | * @Type("Doctrine\Common\Collections\ArrayCollection<string>") |
||
135 | * @Assert\Type("Doctrine\Common\Collections\ArrayCollection") |
||
136 | * @var ArrayCollection |
||
137 | */ |
||
138 | private $implements; |
||
139 | |||
140 | /** |
||
141 | * Construct |
||
142 | */ |
||
143 | public function __construct() |
||
149 | |||
150 | /** |
||
151 | * @Assert\IsTrue(message = "Duplicated properties names in entity, check yaml schema!") |
||
152 | * @return boolean |
||
153 | */ |
||
154 | public function hasUniquePropertiesNames() |
||
169 | |||
170 | /** |
||
171 | * @Assert\IsTrue(message = "Invalid class namespace, check yaml schema! eg. \AppBundle\Vendor\Entity") |
||
172 | * @return boolean |
||
173 | */ |
||
174 | public function isValidNamespace() |
||
178 | |||
179 | /** |
||
180 | * @Assert\IsTrue(message = "Invalid base class namespace, check yaml schema! eg. \AppBundle\Vendor\Entity") |
||
181 | * @return boolean |
||
182 | */ |
||
183 | public function isValidExtends() |
||
199 | |||
200 | /** |
||
201 | * @return ArrayCollection |
||
202 | */ |
||
203 | public function getMethods() |
||
207 | |||
208 | /** |
||
209 | * @param ArrayCollection $methods |
||
210 | * @return InterfaceManager |
||
211 | */ |
||
212 | public function setMethods(ArrayCollection $methods) |
||
217 | |||
218 | /** |
||
219 | * @return ArrayCollection |
||
220 | */ |
||
221 | public function getProperties() |
||
225 | |||
226 | /** |
||
227 | * @param ArrayCollection $properties |
||
228 | * @return ClassManager |
||
229 | */ |
||
230 | public function setProperties(ArrayCollection $properties) |
||
235 | |||
236 | /** |
||
237 | * @return InterfaceManager |
||
238 | */ |
||
239 | public function getInterface() |
||
243 | |||
244 | /** |
||
245 | * @param InterfaceManager $interface |
||
246 | * @return ClassManager |
||
247 | */ |
||
248 | public function setInterface(InterfaceManager $interface) |
||
253 | |||
254 | /** |
||
255 | * @return ClassConstructorManager |
||
256 | */ |
||
257 | public function getConstructor() |
||
261 | |||
262 | /** |
||
263 | * @param ClassConstructorManager $constructor |
||
264 | * @return ClassManager |
||
265 | */ |
||
266 | public function setConstructor(ClassConstructorManager $constructor) |
||
271 | |||
272 | /** |
||
273 | * @return string |
||
274 | */ |
||
275 | public function getComment() |
||
279 | |||
280 | /** |
||
281 | * @param string |
||
282 | * @return ClassManager |
||
283 | */ |
||
284 | public function setComment($comment) |
||
289 | |||
290 | /** |
||
291 | * @return TestClassManager |
||
292 | */ |
||
293 | public function getTestClass() |
||
297 | |||
298 | /** |
||
299 | * @param TestClassManager $testClass |
||
300 | * @return ClassManager |
||
301 | */ |
||
302 | public function setTestClass(TestClassManager $testClass) |
||
307 | |||
308 | /** |
||
309 | * @return string |
||
310 | */ |
||
311 | public function getNamespace() |
||
315 | |||
316 | /** |
||
317 | * @param string $namespace |
||
318 | * @return ClassManager |
||
319 | */ |
||
320 | public function setNamespace($namespace) |
||
325 | |||
326 | /** |
||
327 | * Return base class namespace |
||
328 | * |
||
329 | * @return string |
||
330 | */ |
||
331 | public function getExtends() |
||
335 | |||
336 | /** |
||
337 | * Set base class namespace |
||
338 | * |
||
339 | * @param string $extends |
||
340 | */ |
||
341 | public function setExtends($extends) |
||
345 | |||
346 | /** |
||
347 | * has set base class namespace |
||
348 | * |
||
349 | * @return boolean |
||
350 | */ |
||
351 | public function hasExtends() |
||
355 | |||
356 | /** |
||
357 | * Check interface exists |
||
358 | * |
||
359 | * @return boolean |
||
360 | */ |
||
361 | public function hasInterface() |
||
365 | |||
366 | /** |
||
367 | * Check interface exists |
||
368 | * |
||
369 | * @return boolean |
||
370 | */ |
||
371 | public function hasTestClass() |
||
375 | |||
376 | /** |
||
377 | * Return namespace without name - for createing directory |
||
378 | * |
||
379 | * @return string |
||
380 | */ |
||
381 | public function getDirectory() |
||
385 | |||
386 | /** |
||
387 | * Return name of class/interface from namespace |
||
388 | * |
||
389 | * @return string |
||
390 | * @throws Exception |
||
391 | */ |
||
392 | public function getName() |
||
396 | |||
397 | /** |
||
398 | * Return namespace without name - for rendering namespace in class |
||
399 | * |
||
400 | * @return string |
||
401 | * @throws Exception |
||
402 | */ |
||
403 | public function getNamespaceWithoutName() |
||
407 | |||
408 | /** |
||
409 | * Return namespace without name - for rendering namespace in class |
||
410 | * |
||
411 | * @return string |
||
412 | * @throws Exception |
||
413 | */ |
||
414 | public function getNamespaceWithoutNameAndBackslashPrefix() |
||
418 | |||
419 | /** |
||
420 | * Return set of tags used in template |
||
421 | * |
||
422 | * @return array |
||
423 | */ |
||
424 | public function getTemplateTags() |
||
438 | |||
439 | /** |
||
440 | * @return string |
||
441 | */ |
||
442 | public function getClassManagerTemplatePath() |
||
446 | |||
447 | /** |
||
448 | * @return string |
||
449 | */ |
||
450 | public function getClassConstructorManagerTemplatePath() |
||
454 | |||
455 | /** |
||
456 | * @return string |
||
457 | */ |
||
458 | public function getInterfaceManagerTemplatePath() |
||
462 | |||
463 | /** |
||
464 | * @return string |
||
465 | */ |
||
466 | public function getTestClassManagerTemplatePath() |
||
470 | |||
471 | /** |
||
472 | * @param string $classManagerTemplatePath |
||
473 | * @return $this |
||
474 | */ |
||
475 | public function setClassManagerTemplatePath($classManagerTemplatePath) |
||
480 | |||
481 | /** |
||
482 | * @param string $classConstructorManagerTemplatePath |
||
483 | * @return $this |
||
484 | */ |
||
485 | public function setClassConstructorManagerTemplatePath($classConstructorManagerTemplatePath) |
||
490 | |||
491 | /** |
||
492 | * @param string $interfaceManagerTemplatePath |
||
493 | * @return $this |
||
494 | */ |
||
495 | public function setInterfaceManagerTemplatePath($interfaceManagerTemplatePath) |
||
500 | |||
501 | /** |
||
502 | * @param string $testClassManagerTemplatePath |
||
503 | * @return $this |
||
504 | */ |
||
505 | public function setTestClassManagerTemplatePath($testClassManagerTemplatePath) |
||
510 | |||
511 | /** |
||
512 | * @return \SimpleEntityGeneratorBundle\Lib\ClassConfig |
||
513 | */ |
||
514 | public function getConfiguration() |
||
518 | |||
519 | /** |
||
520 | * @param \SimpleEntityGeneratorBundle\Lib\ClassConfig $configuration |
||
521 | * @return \SimpleEntityGeneratorBundle\Lib\Items\ClassManager |
||
522 | */ |
||
523 | public function setConfiguration(\SimpleEntityGeneratorBundle\Lib\ClassConfig $configuration) |
||
528 | |||
529 | /** |
||
530 | * @return ArrayCollection |
||
531 | */ |
||
532 | public function getImplements() |
||
540 | |||
541 | /** |
||
542 | * @param ArrayCollection $implements |
||
543 | * @return ClassManager |
||
544 | */ |
||
545 | public function setImplements(ArrayCollection $implements) |
||
550 | } |
||
551 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.