Complex classes like ManagingProductsContext 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 ManagingProductsContext, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
39 | final class ManagingProductsContext implements Context |
||
40 | { |
||
41 | /** |
||
42 | * @var SharedStorageInterface |
||
43 | */ |
||
44 | private $sharedStorage; |
||
45 | |||
46 | /**x |
||
47 | * @var CreateSimpleProductPageInterface |
||
48 | */ |
||
49 | private $createSimpleProductPage; |
||
50 | |||
51 | /** |
||
52 | * @var CreateConfigurableProductPageInterface |
||
53 | */ |
||
54 | private $createConfigurableProductPage; |
||
55 | |||
56 | /** |
||
57 | * @var IndexPageInterface |
||
58 | */ |
||
59 | private $indexPage; |
||
60 | |||
61 | /** |
||
62 | * @var UpdateSimpleProductPageInterface |
||
63 | */ |
||
64 | private $updateSimpleProductPage; |
||
65 | |||
66 | /** |
||
67 | * @var UpdateConfigurableProductPageInterface |
||
68 | */ |
||
69 | private $updateConfigurableProductPage; |
||
70 | |||
71 | /** |
||
72 | * @var ProductReviewIndexPageInterface |
||
73 | */ |
||
74 | private $productReviewIndexPage; |
||
75 | |||
76 | /** |
||
77 | * @var IndexPerTaxonPageInterface |
||
78 | */ |
||
79 | private $indexPerTaxonPage; |
||
80 | |||
81 | /** |
||
82 | * @var CurrentPageResolverInterface |
||
83 | */ |
||
84 | private $currentPageResolver; |
||
85 | |||
86 | /** |
||
87 | * @var NotificationCheckerInterface |
||
88 | */ |
||
89 | private $notificationChecker; |
||
90 | |||
91 | /** |
||
92 | * @param SharedStorageInterface $sharedStorage |
||
93 | * @param CreateSimpleProductPageInterface $createSimpleProductPage |
||
94 | * @param CreateConfigurableProductPageInterface $createConfigurableProductPage |
||
95 | * @param IndexPageInterface $indexPage |
||
96 | * @param UpdateSimpleProductPageInterface $updateSimpleProductPage |
||
97 | * @param UpdateConfigurableProductPageInterface $updateConfigurableProductPage |
||
98 | * @param ProductReviewIndexPageInterface $productReviewIndexPage |
||
99 | * @param IndexPerTaxonPageInterface $indexPerTaxonPage |
||
100 | * @param CurrentPageResolverInterface $currentPageResolver |
||
101 | * @param NotificationCheckerInterface $notificationChecker |
||
102 | */ |
||
103 | public function __construct( |
||
126 | |||
127 | /** |
||
128 | * @Given I want to create a new simple product |
||
129 | */ |
||
130 | public function iWantToCreateANewSimpleProduct() |
||
134 | |||
135 | /** |
||
136 | * @Given I want to create a new configurable product |
||
137 | */ |
||
138 | public function iWantToCreateANewConfigurableProduct() |
||
142 | |||
143 | /** |
||
144 | * @When I specify its code as :code |
||
145 | * @When I do not specify its code |
||
146 | */ |
||
147 | public function iSpecifyItsCodeAs($code = null) |
||
153 | |||
154 | /** |
||
155 | * @When I name it :name in :language |
||
156 | * @When I rename it to :name in :language |
||
157 | */ |
||
158 | public function iRenameItToIn($name, $language) |
||
164 | |||
165 | /** |
||
166 | * @When I add it |
||
167 | * @When I try to add it |
||
168 | */ |
||
169 | public function iAddIt() |
||
176 | |||
177 | /** |
||
178 | * @When I disable its inventory tracking |
||
179 | */ |
||
180 | public function iDisableItsTracking() |
||
184 | |||
185 | /** |
||
186 | * @When I enable its inventory tracking |
||
187 | */ |
||
188 | public function iEnableItsTracking() |
||
192 | |||
193 | /** |
||
194 | * @When /^I set its(?:| default) price to "(?:€|£|\$)([^"]+)" for "([^"]+)" channel$/ |
||
195 | */ |
||
196 | public function iSetItsPriceTo($price, $channelName) |
||
200 | |||
201 | /** |
||
202 | * @When I make it available in channel :channel |
||
203 | */ |
||
204 | public function iMakeItAvailableInChannel($channel) |
||
208 | |||
209 | /** |
||
210 | * @When I assign it to channel :channel |
||
211 | */ |
||
212 | public function iAssignItToChannel($channel) |
||
217 | |||
218 | /** |
||
219 | * @When I choose :calculatorName calculator |
||
220 | */ |
||
221 | public function iChooseCalculator($calculatorName) |
||
225 | |||
226 | /** |
||
227 | * @When I set its slug to :slug |
||
228 | * @When I set its slug to :slug in :language |
||
229 | * @When I remove its slug |
||
230 | */ |
||
231 | public function iSetItsSlugToIn($slug = null, $language = 'en_US') |
||
235 | |||
236 | /** |
||
237 | * @When I enable slug modification |
||
238 | * @When I enable slug modification in :localeCode |
||
239 | */ |
||
240 | public function iEnableSlugModification($localeCode = 'en_US') |
||
245 | |||
246 | /** |
||
247 | * @Then the product :productName should appear in the store |
||
248 | * @Then the product :productName should be in the shop |
||
249 | * @Then this product should still be named :productName |
||
250 | */ |
||
251 | public function theProductShouldAppearInTheShop($productName) |
||
257 | |||
258 | /** |
||
259 | * @Given I am browsing products |
||
260 | * @When I want to browse products |
||
261 | */ |
||
262 | public function iWantToBrowseProducts() |
||
266 | |||
267 | /** |
||
268 | * @When /^I am browsing products from ("([^"]+)" taxon)$/ |
||
269 | */ |
||
270 | public function iAmBrowsingProductsFromTaxon(TaxonInterface $taxon) |
||
274 | |||
275 | /** |
||
276 | * @When I filter them by :taxonName taxon |
||
277 | */ |
||
278 | public function iFilterThemByTaxon($taxonName) |
||
282 | |||
283 | /** |
||
284 | * @Then I should( still) see a product with :field :value |
||
285 | */ |
||
286 | public function iShouldSeeProductWith($field, $value) |
||
290 | |||
291 | /** |
||
292 | * @Then I should not see any product with :field :value |
||
293 | */ |
||
294 | public function iShouldNotSeeAnyProductWith($field, $value) |
||
298 | |||
299 | /** |
||
300 | * @Then the first product on the list should have :field :value |
||
301 | */ |
||
302 | public function theFirstProductOnTheListShouldHave($field, $value) |
||
308 | |||
309 | /** |
||
310 | * @Then the last product on the list should have :field :value |
||
311 | */ |
||
312 | public function theLastProductOnTheListShouldHave($field, $value) |
||
318 | |||
319 | /** |
||
320 | * @When I switch the way products are sorted by :field |
||
321 | * @When I start sorting products by :field |
||
322 | * @Given the products are already sorted by :field |
||
323 | */ |
||
324 | public function iSortProductsBy($field) |
||
328 | |||
329 | /** |
||
330 | * @Then I should see :numberOfProducts products in the list |
||
331 | */ |
||
332 | public function iShouldSeeProductsInTheList($numberOfProducts) |
||
336 | |||
337 | /** |
||
338 | * @When I delete the :product product |
||
339 | * @When I try to delete the :product product |
||
340 | */ |
||
341 | public function iDeleteProduct(ProductInterface $product) |
||
348 | |||
349 | /** |
||
350 | * @Then /^(this product) should not exist in the product catalog$/ |
||
351 | */ |
||
352 | public function productShouldNotExist(ProductInterface $product) |
||
358 | |||
359 | /** |
||
360 | * @Then I should be notified that this product is in use and cannot be deleted |
||
361 | */ |
||
362 | public function iShouldBeNotifiedOfFailure() |
||
369 | |||
370 | /** |
||
371 | * @Then /^(this product) should still exist in the product catalog$/ |
||
372 | */ |
||
373 | public function productShouldExistInTheProductCatalog(ProductInterface $product) |
||
377 | |||
378 | /** |
||
379 | * @When I want to modify the :product product |
||
380 | * @When /^I want to modify (this product)$/ |
||
381 | */ |
||
382 | public function iWantToModifyAProduct(ProductInterface $product) |
||
393 | |||
394 | /** |
||
395 | * @Then the code field should be disabled |
||
396 | */ |
||
397 | public function theCodeFieldShouldBeDisabled() |
||
403 | |||
404 | /** |
||
405 | * @Then the slug field should not be editable |
||
406 | * @Then the slug field in :localeCode (also )should not be editable |
||
407 | */ |
||
408 | public function theSlugFieldShouldNotBeEditable($localeCode = 'en_US') |
||
412 | |||
413 | /** |
||
414 | * @Then this product name should be :name |
||
415 | */ |
||
416 | public function thisProductElementShouldBe($name) |
||
420 | |||
421 | /** |
||
422 | * @Then /^I should be notified that (code|name|slug) is required$/ |
||
423 | */ |
||
424 | public function iShouldBeNotifiedThatIsRequired($element) |
||
428 | |||
429 | /** |
||
430 | * @When I save my changes |
||
431 | * @When I try to save my changes |
||
432 | */ |
||
433 | public function iSaveMyChanges() |
||
440 | |||
441 | /** |
||
442 | * @When /^I change its price to (?:€|£|\$)([^"]+) for "([^"]+)" channel$/ |
||
443 | */ |
||
444 | public function iChangeItsPriceTo($price, $channelName) |
||
448 | |||
449 | /** |
||
450 | * @Given I add the :optionName option to it |
||
451 | */ |
||
452 | public function iAddTheOptionToIt($optionName) |
||
456 | |||
457 | /** |
||
458 | * @When I set its :attribute attribute to :value |
||
459 | * @When I set its :attribute attribute to :value in :language |
||
460 | */ |
||
461 | public function iSetItsAttributeTo($attribute, $value, $language = 'en_US') |
||
465 | |||
466 | /** |
||
467 | * @When I remove its :attribute attribute |
||
468 | * @When I remove its :attribute attribute from :language |
||
469 | */ |
||
470 | public function iRemoveItsAttribute($attribute, $language = 'en_US') |
||
474 | |||
475 | /** |
||
476 | * @When I try to add new attributes |
||
477 | */ |
||
478 | public function iTryToAddNewAttributes() |
||
482 | |||
483 | /** |
||
484 | * @Then attribute :attributeName of product :product should be :value |
||
485 | * @Then attribute :attributeName of product :product should be :value in :language |
||
486 | */ |
||
487 | public function itsAttributeShouldBe($attributeName, ProductInterface $product, $value, $language = 'en_US') |
||
493 | |||
494 | /** |
||
495 | * @Then /^(product "[^"]+") should not have a "([^"]+)" attribute$/ |
||
496 | */ |
||
497 | public function productShouldNotHaveAttribute(ProductInterface $product, $attribute) |
||
503 | |||
504 | /** |
||
505 | * @Then product :product should not have any attributes |
||
506 | * @Then product :product should have :count attribute |
||
507 | */ |
||
508 | public function productShouldNotHaveAnyAttributes(ProductInterface $product, $count = 0) |
||
512 | |||
513 | /** |
||
514 | * @Given product with :element :value should not be added |
||
515 | */ |
||
516 | public function productWithNameShouldNotBeAdded($element, $value) |
||
522 | |||
523 | /** |
||
524 | * @When I remove its name from :language translation |
||
525 | */ |
||
526 | public function iRemoveItsNameFromTranslation($language) |
||
532 | |||
533 | /** |
||
534 | * @Then /^this product should have (?:a|an) "([^"]+)" option$/ |
||
535 | */ |
||
536 | public function thisProductShouldHaveOption($productOption) |
||
540 | |||
541 | /** |
||
542 | * @Then the option field should be disabled |
||
543 | */ |
||
544 | public function theOptionFieldShouldBeDisabled() |
||
548 | |||
549 | /** |
||
550 | * @When /^I choose main (taxon "([^"]+)")$/ |
||
551 | */ |
||
552 | public function iChooseMainTaxon(TaxonInterface $taxon) |
||
558 | |||
559 | /** |
||
560 | * @Then /^the slug of the ("[^"]+" product) should(?:| still) be "([^"]+)"$/ |
||
561 | * @Then /^the slug of the ("[^"]+" product) should(?:| still) be "([^"]+)" (in the "[^"]+" locale)$/ |
||
562 | */ |
||
563 | public function productSlugShouldBe(ProductInterface $product, $slug, $locale = 'en_US') |
||
569 | |||
570 | /** |
||
571 | * @Then /^(this product) main taxon should be "([^"]+)"$/ |
||
572 | */ |
||
573 | public function thisProductMainTaxonShouldBe(ProductInterface $product, $taxonName) |
||
580 | |||
581 | /** |
||
582 | * @Then /^inventory of (this product) should not be tracked$/ |
||
583 | */ |
||
584 | public function thisProductShouldNotBeTracked(ProductInterface $product) |
||
590 | |||
591 | /** |
||
592 | * @Then /^inventory of (this product) should be tracked$/ |
||
593 | */ |
||
594 | public function thisProductShouldBeTracked(ProductInterface $product) |
||
600 | |||
601 | /** |
||
602 | * @When I attach the :path image with a code :code |
||
603 | */ |
||
604 | public function iAttachImageWithACode($path, $code) |
||
611 | |||
612 | /** |
||
613 | * @When I attach the :path image without a code |
||
614 | */ |
||
615 | public function iAttachImageWithoutACode($path) |
||
622 | |||
623 | /** |
||
624 | * @When I associate as :productAssociationType the :productName product |
||
625 | * @When I associate as :productAssociationType the :firstProductName and :secondProductName products |
||
626 | */ |
||
627 | public function iAssociateProductsAsProductAssociation( |
||
636 | |||
637 | /** |
||
638 | * @When I remove an associated product :productName from :productAssociationType |
||
639 | */ |
||
640 | public function iRemoveAnAssociatedProductFromProductAssociation( |
||
649 | |||
650 | /** |
||
651 | * @Then /^(this product) should have(?:| also) an image with a code "([^"]*)"$/ |
||
652 | * @Then /^the (product "[^"]+") should have(?:| also) an image with a code "([^"]*)"$/ |
||
653 | */ |
||
654 | public function thisProductShouldHaveAnImageWithCode(ProductInterface $product, $code) |
||
663 | |||
664 | /** |
||
665 | * @Then /^this product should not have(?:| also) an image with a code "([^"]*)"$/ |
||
666 | */ |
||
667 | public function thisProductShouldNotHaveAnImageWithCode($code) |
||
674 | |||
675 | /** |
||
676 | * @When I change the image with the :code code to :path |
||
677 | */ |
||
678 | public function iChangeItsImageToPathForTheCode($path, $code) |
||
685 | |||
686 | /** |
||
687 | * @When /^I remove(?:| also) an image with a code "([^"]*)"$/ |
||
688 | */ |
||
689 | public function iRemoveAnImageWithACode($code) |
||
696 | |||
697 | /** |
||
698 | * @When I remove the first image |
||
699 | */ |
||
700 | public function iRemoveTheFirstImage() |
||
707 | |||
708 | /** |
||
709 | * @Then /^(this product) should not have any images$/ |
||
710 | */ |
||
711 | public function thisProductShouldNotHaveImages(ProductInterface $product) |
||
719 | |||
720 | /** |
||
721 | * @Then the image code field should be disabled |
||
722 | */ |
||
723 | public function theImageCodeFieldShouldBeDisabled() |
||
730 | |||
731 | /** |
||
732 | * @Then I should be notified that the image with this code already exists |
||
733 | */ |
||
734 | public function iShouldBeNotifiedThatTheImageWithThisCodeAlreadyExists() |
||
738 | |||
739 | /** |
||
740 | * @Then I should be notified that an image code is required |
||
741 | */ |
||
742 | public function iShouldBeNotifiedThatAnImageCodeIsRequired() |
||
749 | |||
750 | /** |
||
751 | * @Then there should still be only one image in the :product product |
||
752 | */ |
||
753 | public function thereShouldStillBeOnlyOneImageInThisTaxon(ProductInterface $product) |
||
761 | |||
762 | /** |
||
763 | * @Then /^there should be no reviews of (this product)$/ |
||
764 | */ |
||
765 | public function thereAreNoProductReviews(ProductInterface $product) |
||
771 | |||
772 | /** |
||
773 | * @Then this product should( also) have an association :productAssociationType with product :productName |
||
774 | * @Then this product should( also) have an association :productAssociationType with products :firstProductName and :secondProductName |
||
775 | */ |
||
776 | public function theProductShouldHaveAnAssociationWithProducts( |
||
791 | |||
792 | /** |
||
793 | * @Then this product should not have an association :productAssociationType with product :productName |
||
794 | */ |
||
795 | public function theProductShouldNotHaveAnAssociationWithProducts( |
||
801 | |||
802 | /** |
||
803 | * @Then I should be notified that simple product code has to be unique |
||
804 | */ |
||
805 | public function iShouldBeNotifiedThatSimpleProductCodeHasToBeUnique() |
||
809 | |||
810 | /** |
||
811 | * @Then I should not be notified that simple product code has to be unique |
||
812 | */ |
||
813 | public function iShouldNotBeNotifiedThatSimpleProductCodeHasToBeUnique() |
||
826 | |||
827 | /** |
||
828 | * @Then I should be notified that slug has to be unique |
||
829 | */ |
||
830 | public function iShouldBeNotifiedThatSlugHasToBeUnique() |
||
834 | |||
835 | /** |
||
836 | * @Then I should be notified that code has to be unique |
||
837 | */ |
||
838 | public function iShouldBeNotifiedThatCodeHasToBeUnique() |
||
842 | |||
843 | /** |
||
844 | * @Then I should be notified that price must be defined for every channel |
||
845 | */ |
||
846 | public function iShouldBeNotifiedThatPriceMustBeDefinedForEveryChannel() |
||
850 | |||
851 | /** |
||
852 | * @Then they should have order like :firstProductName, :secondProductName and :thirdProductName |
||
853 | */ |
||
854 | public function theyShouldHaveOrderLikeAnd(...$productNames) |
||
858 | |||
859 | /** |
||
860 | * @When I save my new configuration |
||
861 | */ |
||
862 | public function iSaveMyNewConfiguration() |
||
866 | |||
867 | /** |
||
868 | * @When I set the position of :productName to :position |
||
869 | */ |
||
870 | public function iSetThePositionOfTo($productName, $position) |
||
874 | |||
875 | /** |
||
876 | * @Then this product should( still) have slug :value in :language |
||
877 | */ |
||
878 | public function thisProductElementShouldHaveSlugIn($slug, $language) |
||
882 | |||
883 | /** |
||
884 | * @When I set its shipping category as :shippingCategoryName |
||
885 | */ |
||
886 | public function iSetItsShippingCategoryAs($shippingCategoryName) |
||
890 | |||
891 | /** |
||
892 | * @Then /^(it|this product) should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/ |
||
893 | * @Then /^(product "[^"]+") should be priced at (?:€|£|\$)([^"]+) for channel "([^"]+)"$/ |
||
894 | */ |
||
895 | public function itShouldBePricedAtForChannel(ProductInterface $product, $price, $channelName) |
||
901 | |||
902 | /** |
||
903 | * @Then /^(this product) should no longer have price for channel "([^"]+)"$/ |
||
904 | */ |
||
905 | public function thisProductShouldNoLongerHavePriceForChannel(ProductInterface $product, $channelName) |
||
919 | |||
920 | /** |
||
921 | * @Then I should be notified that the :imageNumber image should have an unique code |
||
922 | */ |
||
923 | public function iShouldBeNotifiedThatTheFirstImageShouldHaveAnUniqueCode($imageNumber) |
||
930 | |||
931 | /** |
||
932 | * @Then I should be notified that I have to define product variants' prices for newly assigned channels first |
||
933 | */ |
||
934 | public function iShouldBeNotifiedThatIHaveToDefineProductVariantsPricesForNewlyAssignedChannelsFirst() |
||
941 | |||
942 | /** |
||
943 | * @param string $element |
||
944 | * @param string $value |
||
945 | */ |
||
946 | private function assertElementValue($element, $value) |
||
958 | |||
959 | /** |
||
960 | * @param string $element |
||
961 | * @param string $message |
||
962 | */ |
||
963 | private function assertValidationMessage($element, $message) |
||
970 | |||
971 | /** |
||
972 | * @return IndexPageInterface|IndexPerTaxonPageInterface|CreateSimpleProductPageInterface|CreateConfigurableProductPageInterface|UpdateSimpleProductPageInterface|UpdateConfigurableProductPageInterface |
||
973 | */ |
||
974 | private function resolveCurrentPage() |
||
985 | } |
||
986 |
It seems like the method you are trying to call exists only in some of the possible types.
Let’s take a look at an example:
Available Fixes
Add an additional type-check:
Only allow a single type to be passed if the variable comes from a parameter: