Complex classes like Product 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 Product, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class Product implements ProductInterface |
||
29 | { |
||
30 | use TimestampableTrait, ToggleableTrait; |
||
31 | use TranslatableTrait { |
||
32 | __construct as private initializeTranslationsCollection; |
||
33 | } |
||
34 | |||
35 | /** |
||
36 | * @var mixed |
||
37 | */ |
||
38 | protected $id; |
||
39 | |||
40 | /** |
||
41 | * @var string |
||
42 | */ |
||
43 | protected $code; |
||
44 | |||
45 | /** |
||
46 | * @var Collection|AttributeValueInterface[] |
||
47 | */ |
||
48 | protected $attributes; |
||
49 | |||
50 | /** |
||
51 | * @var Collection|ProductVariantInterface[] |
||
52 | */ |
||
53 | protected $variants; |
||
54 | |||
55 | /** |
||
56 | * @var Collection|ProductOptionInterface[] |
||
57 | */ |
||
58 | protected $options; |
||
59 | |||
60 | /** |
||
61 | * @var Collection|ProductAssociationInterface[] |
||
62 | */ |
||
63 | protected $associations; |
||
64 | |||
65 | public function __construct() |
||
75 | |||
76 | /** |
||
77 | * @return string |
||
78 | */ |
||
79 | public function __toString(): string |
||
83 | |||
84 | /** |
||
85 | * {@inheritdoc} |
||
86 | */ |
||
87 | public function getId() |
||
91 | |||
92 | /** |
||
93 | * @return string |
||
94 | */ |
||
95 | public function getCode(): ?string |
||
99 | |||
100 | /** |
||
101 | * @param string $code |
||
102 | */ |
||
103 | public function setCode(?string $code): void |
||
107 | |||
108 | /** |
||
109 | * {@inheritdoc} |
||
110 | */ |
||
111 | public function getName(): ?string |
||
115 | |||
116 | /** |
||
117 | * {@inheritdoc} |
||
118 | */ |
||
119 | public function setName(?string $name): void |
||
123 | |||
124 | /** |
||
125 | * {@inheritdoc} |
||
126 | */ |
||
127 | public function getSlug(): ?string |
||
131 | |||
132 | /** |
||
133 | * {@inheritdoc} |
||
134 | */ |
||
135 | public function setSlug(?string $slug): void |
||
139 | |||
140 | /** |
||
141 | * {@inheritdoc} |
||
142 | */ |
||
143 | public function getDescription(): ?string |
||
147 | |||
148 | /** |
||
149 | * {@inheritdoc} |
||
150 | */ |
||
151 | public function setDescription(?string $description): void |
||
155 | |||
156 | /** |
||
157 | * {@inheritdoc} |
||
158 | */ |
||
159 | public function getMetaKeywords(): ?string |
||
163 | |||
164 | /** |
||
165 | * {@inheritdoc} |
||
166 | */ |
||
167 | public function setMetaKeywords(?string $metaKeywords): void |
||
171 | |||
172 | /** |
||
173 | * {@inheritdoc} |
||
174 | */ |
||
175 | public function getMetaDescription(): ?string |
||
179 | |||
180 | /** |
||
181 | * {@inheritdoc} |
||
182 | */ |
||
183 | public function setMetaDescription(?string $metaDescription): void |
||
187 | |||
188 | public function getAttributes(): Collection |
||
192 | |||
193 | /** |
||
194 | * {@inheritdoc} |
||
195 | */ |
||
196 | public function getAttributesByLocale( |
||
219 | |||
220 | /** |
||
221 | * {@inheritdoc} |
||
222 | */ |
||
223 | public function addAttribute(?AttributeValueInterface $attribute): void |
||
236 | |||
237 | /** |
||
238 | * {@inheritdoc} |
||
239 | */ |
||
240 | public function removeAttribute(?AttributeValueInterface $attribute): void |
||
253 | |||
254 | /** |
||
255 | * {@inheritdoc} |
||
256 | */ |
||
257 | public function hasAttribute(AttributeValueInterface $attribute): bool |
||
261 | |||
262 | /** |
||
263 | * {@inheritdoc} |
||
264 | */ |
||
265 | public function hasAttributeByCodeAndLocale(string $attributeCode, ?string $localeCode = null): bool |
||
278 | |||
279 | /** |
||
280 | * {@inheritdoc} |
||
281 | */ |
||
282 | public function getAttributeByCodeAndLocale(string $attributeCode, ?string $localeCode = null): ?AttributeValueInterface |
||
297 | |||
298 | /** |
||
299 | * {@inheritdoc} |
||
300 | */ |
||
301 | public function hasVariants(): bool |
||
305 | |||
306 | /** |
||
307 | * {@inheritdoc} |
||
308 | */ |
||
309 | public function getVariants(): Collection |
||
313 | |||
314 | /** |
||
315 | * {@inheritdoc} |
||
316 | */ |
||
317 | public function addVariant(ProductVariantInterface $variant): void |
||
324 | |||
325 | /** |
||
326 | * {@inheritdoc} |
||
327 | */ |
||
328 | public function removeVariant(ProductVariantInterface $variant): void |
||
335 | |||
336 | /** |
||
337 | * {@inheritdoc} |
||
338 | */ |
||
339 | public function hasVariant(ProductVariantInterface $variant): bool |
||
343 | |||
344 | /** |
||
345 | * {@inheritdoc} |
||
346 | */ |
||
347 | public function hasOptions(): bool |
||
351 | |||
352 | /** |
||
353 | * {@inheritdoc} |
||
354 | */ |
||
355 | public function getOptions(): Collection |
||
359 | |||
360 | /** |
||
361 | * {@inheritdoc} |
||
362 | */ |
||
363 | public function addOption(ProductOptionInterface $option): void |
||
369 | |||
370 | /** |
||
371 | * {@inheritdoc} |
||
372 | */ |
||
373 | public function removeOption(ProductOptionInterface $option): void |
||
379 | |||
380 | /** |
||
381 | * {@inheritdoc} |
||
382 | */ |
||
383 | public function hasOption(ProductOptionInterface $option): bool |
||
387 | |||
388 | /** |
||
389 | * {@inheritdoc} |
||
390 | */ |
||
391 | public function getAssociations(): Collection |
||
395 | |||
396 | /** |
||
397 | * {@inheritdoc} |
||
398 | */ |
||
399 | public function addAssociation(ProductAssociationInterface $association): void |
||
406 | |||
407 | /** |
||
408 | * {@inheritdoc} |
||
409 | */ |
||
410 | public function removeAssociation(ProductAssociationInterface $association): void |
||
417 | |||
418 | /** |
||
419 | * {@inheritdoc} |
||
420 | */ |
||
421 | public function hasAssociation(ProductAssociationInterface $association): bool |
||
425 | |||
426 | /** |
||
427 | * {@inheritdoc} |
||
428 | */ |
||
429 | public function isSimple(): bool |
||
433 | |||
434 | /** |
||
435 | * {@inheritdoc} |
||
436 | */ |
||
437 | public function isConfigurable(): bool |
||
441 | |||
442 | /** |
||
443 | * {@inheritdoc} |
||
444 | */ |
||
445 | protected function createTranslation(): ProductTranslationInterface |
||
449 | |||
450 | /** |
||
451 | * @param ProductAttributeValueInterface $attributeValue |
||
452 | * @param string $localeCode |
||
453 | * @param string|null $fallbackLocaleCode |
||
454 | * |
||
455 | * @return AttributeValueInterface |
||
456 | */ |
||
457 | private function getAttributeInDifferentLocale( |
||
475 | |||
476 | /** |
||
477 | * @param string $attributeCode |
||
478 | * @param string $localeCode |
||
479 | * |
||
480 | * @return bool |
||
481 | */ |
||
482 | private function hasNotEmptyAttributeByCodeAndLocale(string $attributeCode, string $localeCode): bool |
||
496 | } |
||
497 |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the interface: