Complex classes like CartContext 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 CartContext, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 27 | final class CartContext implements Context |
||
| 28 | { |
||
| 29 | /** @var SharedStorageInterface */ |
||
| 30 | private $sharedStorage; |
||
| 31 | |||
| 32 | /** @var SummaryPageInterface */ |
||
| 33 | private $summaryPage; |
||
| 34 | |||
| 35 | /** @var ShowPageInterface */ |
||
| 36 | private $productShowPage; |
||
| 37 | |||
| 38 | /** @var NotificationCheckerInterface */ |
||
| 39 | private $notificationChecker; |
||
| 40 | |||
| 41 | public function __construct( |
||
| 52 | |||
| 53 | /** |
||
| 54 | * @When I see the summary of my cart |
||
| 55 | */ |
||
| 56 | public function iOpenCartSummaryPage() |
||
| 60 | |||
| 61 | /** |
||
| 62 | * @When I update my cart |
||
| 63 | */ |
||
| 64 | public function iUpdateMyCart() |
||
| 68 | |||
| 69 | /** |
||
| 70 | * @Then my cart should be empty |
||
| 71 | * @Then cart should be empty with no value |
||
| 72 | */ |
||
| 73 | public function iShouldBeNotifiedThatMyCartIsEmpty() |
||
| 79 | |||
| 80 | /** |
||
| 81 | * @Given I removed product :productName from the cart |
||
| 82 | * @When I remove product :productName from the cart |
||
| 83 | */ |
||
| 84 | public function iRemoveProductFromTheCart(string $productName): void |
||
| 89 | |||
| 90 | /** |
||
| 91 | * @Given I change :productName quantity to :quantity |
||
| 92 | */ |
||
| 93 | public function iChangeQuantityTo($productName, $quantity) |
||
| 98 | |||
| 99 | /** |
||
| 100 | * @Then the grand total value should be :total |
||
| 101 | * @Then my cart total should be :total |
||
| 102 | */ |
||
| 103 | public function myCartTotalShouldBe($total) |
||
| 109 | |||
| 110 | /** |
||
| 111 | * @Then the grand total value in base currency should be :total |
||
| 112 | */ |
||
| 113 | public function myBaseCartTotalShouldBe($total) |
||
| 119 | |||
| 120 | /** |
||
| 121 | * @Then my cart taxes should be :taxTotal |
||
| 122 | */ |
||
| 123 | public function myCartTaxesShouldBe(string $taxTotal): void |
||
| 129 | |||
| 130 | /** |
||
| 131 | * @Then my included in price taxes should be :taxTotal |
||
| 132 | */ |
||
| 133 | public function myIncludedInPriceTaxesShouldBe(string $taxTotal): void |
||
| 139 | |||
| 140 | /** |
||
| 141 | * @Then there should be no taxes charged |
||
| 142 | */ |
||
| 143 | public function thereShouldBeNoTaxesCharged(): void |
||
| 149 | |||
| 150 | /** |
||
| 151 | * @Then my cart shipping total should be :shippingTotal |
||
| 152 | * @Then my cart shipping should be for free |
||
| 153 | */ |
||
| 154 | public function myCartShippingFeeShouldBe($shippingTotal = '$0.00') |
||
| 160 | |||
| 161 | /** |
||
| 162 | * @Then my discount should be :promotionsTotal |
||
| 163 | */ |
||
| 164 | public function myDiscountShouldBe($promotionsTotal) |
||
| 170 | |||
| 171 | /** |
||
| 172 | * @Given /^there should be no shipping fee$/ |
||
| 173 | */ |
||
| 174 | public function thereShouldBeNoShippingFee() |
||
| 186 | |||
| 187 | /** |
||
| 188 | * @Given /^there should be no discount$/ |
||
| 189 | */ |
||
| 190 | public function thereShouldBeNoDiscount() |
||
| 202 | |||
| 203 | /** |
||
| 204 | * @Then /^(its|theirs) price should be decreased by ("[^"]+")$/ |
||
| 205 | * @Then /^(product "[^"]+") price should be decreased by ("[^"]+")$/ |
||
| 206 | */ |
||
| 207 | public function itsPriceShouldBeDecreasedBy(ProductInterface $product, $amount) |
||
| 217 | |||
| 218 | /** |
||
| 219 | * @Then /^(product "[^"]+") price should not be decreased$/ |
||
| 220 | */ |
||
| 221 | public function productPriceShouldNotBeDecreased(ProductInterface $product) |
||
| 227 | |||
| 228 | /** |
||
| 229 | * @Given /^I (?:add|added) (this product) to the cart$/ |
||
| 230 | * @Given I added product :product to the cart |
||
| 231 | * @Given /^I (?:have|had) (product "[^"]+") in the cart$/ |
||
| 232 | * @When I add product :product to the cart |
||
| 233 | */ |
||
| 234 | public function iAddProductToTheCart(ProductInterface $product) |
||
| 241 | |||
| 242 | /** |
||
| 243 | * @When /^I add (products "([^"]+)" and "([^"]+)") to the cart$/ |
||
| 244 | * @When /^I add (products "([^"]+)", "([^"]+)" and "([^"]+)") to the cart$/ |
||
| 245 | */ |
||
| 246 | public function iAddMultipleProductsToTheCart(array $products) |
||
| 252 | |||
| 253 | /** |
||
| 254 | * @When I add :variantName variant of product :product to the cart |
||
| 255 | * @When /^I add "([^"]+)" variant of (this product) to the cart$/ |
||
| 256 | * @Given I have :variantName variant of product :product in the cart |
||
| 257 | */ |
||
| 258 | public function iAddProductToTheCartSelectingVariant($variantName, ProductInterface $product) |
||
| 265 | |||
| 266 | /** |
||
| 267 | * @When /^I add (\d+) of (them) to (?:the|my) cart$/ |
||
| 268 | */ |
||
| 269 | public function iAddQuantityOfProductsToTheCart($quantity, ProductInterface $product) |
||
| 274 | |||
| 275 | /** |
||
| 276 | * @Given /^I have(?:| added) (\d+) (products "([^"]+)") (?:to|in) the cart$/ |
||
| 277 | * @When /^I add(?:|ed)(?:| again) (\d+) (products "([^"]+)") to the cart$/ |
||
| 278 | */ |
||
| 279 | public function iAddProductsToTheCart($quantity, ProductInterface $product) |
||
| 286 | |||
| 287 | /** |
||
| 288 | * @Then /^I should be(?: on| redirected to) my cart summary page$/ |
||
| 289 | */ |
||
| 290 | public function shouldBeOnMyCartSummaryPage() |
||
| 296 | |||
| 297 | /** |
||
| 298 | * @Then I should be notified that the product has been successfully added |
||
| 299 | */ |
||
| 300 | public function iShouldBeNotifiedThatItHasBeenSuccessfullyAdded() |
||
| 304 | |||
| 305 | /** |
||
| 306 | * @Then there should be one item in my cart |
||
| 307 | */ |
||
| 308 | public function thereShouldBeOneItemInMyCart() |
||
| 312 | |||
| 313 | /** |
||
| 314 | * @Then this item should have name :itemName |
||
| 315 | */ |
||
| 316 | public function thisProductShouldHaveName($itemName) |
||
| 320 | |||
| 321 | /** |
||
| 322 | * @Then this item should have variant :variantName |
||
| 323 | */ |
||
| 324 | public function thisItemShouldHaveVariant($variantName) |
||
| 328 | |||
| 329 | /** |
||
| 330 | * @Then this item should have code :variantCode |
||
| 331 | */ |
||
| 332 | public function thisItemShouldHaveCode($variantCode) |
||
| 336 | |||
| 337 | /** |
||
| 338 | * @Given I have :product with :productOption :productOptionValue in the cart |
||
| 339 | * @When I add :product with :productOption :productOptionValue to the cart |
||
| 340 | */ |
||
| 341 | public function iAddThisProductWithToTheCart(ProductInterface $product, ProductOptionInterface $productOption, $productOptionValue) |
||
| 347 | |||
| 348 | /** |
||
| 349 | * @Given /^(this product) should have ([^"]+) "([^"]+)"$/ |
||
| 350 | */ |
||
| 351 | public function thisItemShouldHaveOptionValue(ProductInterface $product, $optionName, $optionValue) |
||
| 355 | |||
| 356 | /** |
||
| 357 | * @When I clear my cart |
||
| 358 | */ |
||
| 359 | public function iClearMyCart() |
||
| 363 | |||
| 364 | /** |
||
| 365 | * @Then /^I should see "([^"]+)" with quantity (\d+) in my cart$/ |
||
| 366 | */ |
||
| 367 | public function iShouldSeeWithQuantityInMyCart($productName, $quantity) |
||
| 371 | |||
| 372 | /** |
||
| 373 | * @Then /^I should see "([^"]+)" with unit price ("[^"]+") in my cart$/ |
||
| 374 | */ |
||
| 375 | public function iShouldSeeProductWithUnitPriceInMyCart($productName, $unitPrice) |
||
| 379 | |||
| 380 | /** |
||
| 381 | * @Given I use coupon with code :couponCode |
||
| 382 | */ |
||
| 383 | public function iUseCouponWithCode($couponCode) |
||
| 387 | |||
| 388 | /** |
||
| 389 | * @Then I should be notified that the coupon is invalid |
||
| 390 | */ |
||
| 391 | public function iShouldBeNotifiedThatCouponIsInvalid() |
||
| 395 | |||
| 396 | /** |
||
| 397 | * @Then total price of :productName item should be :productPrice |
||
| 398 | */ |
||
| 399 | public function thisItemPriceShouldBe($productName, $productPrice) |
||
| 405 | |||
| 406 | /** |
||
| 407 | * @Then /^I should be notified that (this product) cannot be updated$/ |
||
| 408 | */ |
||
| 409 | public function iShouldBeNotifiedThatThisProductDoesNotHaveSufficientStock(ProductInterface $product) |
||
| 413 | |||
| 414 | /** |
||
| 415 | * @Then /^I should not be notified that (this product) cannot be updated$/ |
||
| 416 | */ |
||
| 417 | public function iShouldNotBeNotifiedThatThisProductCannotBeUpdated(ProductInterface $product) |
||
| 421 | |||
| 422 | /** |
||
| 423 | * @Then my cart's total should be :total |
||
| 424 | */ |
||
| 425 | public function myCartSTotalShouldBe($total) |
||
| 431 | |||
| 432 | /** |
||
| 433 | * @Then /^(\d)(st|nd|rd|th) item in my cart should have "([^"]+)" image displayed$/ |
||
| 434 | */ |
||
| 435 | public function itemShouldHaveImageDisplayed(int $itemNumber, string $image): void |
||
| 439 | |||
| 440 | private function getPriceFromString(string $price): int |
||
| 444 | } |
||
| 445 |
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.
Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.