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 OrderSpec 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 OrderSpec, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 26 | class OrderSpec extends ObjectBehavior |
||
| 27 | { |
||
| 28 | function it_is_initializable() |
||
| 32 | |||
| 33 | function it_implements_Sylius_order_interface() |
||
| 37 | |||
| 38 | function it_implements_Sylius_adjustable_interface() |
||
| 42 | |||
| 43 | function it_implements_Sylius_timestampable_interface() |
||
| 47 | |||
| 48 | function it_has_no_id_by_default() |
||
| 52 | |||
| 53 | function it_is_not_completed_by_default() |
||
| 57 | |||
| 58 | function it_can_be_completed() |
||
| 63 | |||
| 64 | function it_is_completed_when_completion_date_is_set() |
||
| 70 | |||
| 71 | function it_has_no_completion_date_by_default() |
||
| 75 | |||
| 76 | function its_completion_date_is_mutable() |
||
| 83 | |||
| 84 | function it_has_no_number_by_default() |
||
| 88 | |||
| 89 | function its_number_is_mutable() |
||
| 94 | |||
| 95 | function it_creates_items_collection_by_default() |
||
| 99 | |||
| 100 | function it_creates_identities_collection_by_default() |
||
| 104 | |||
| 105 | function it_adds_identities_properly(IdentityInterface $identity) |
||
| 112 | |||
| 113 | function it_adds_items_properly(OrderItemInterface $item) |
||
| 120 | |||
| 121 | function it_removes_identities_properly(IdentityInterface $identity) |
||
| 131 | |||
| 132 | function it_removes_items_properly(OrderItemInterface $item) |
||
| 142 | |||
| 143 | function it_has_items_total_equal_to_0_by_default() |
||
| 147 | |||
| 148 | function it_calculates_correct_items_total( |
||
| 171 | |||
| 172 | function it_creates_adjustments_collection_by_default() |
||
| 176 | |||
| 177 | View Code Duplication | function it_adds_adjustments_properly(AdjustmentInterface $adjustment) |
|
| 178 | { |
||
| 179 | $adjustment->setAdjustable($this)->shouldBeCalled(); |
||
| 180 | $adjustment->isNeutral()->willReturn(true); |
||
| 181 | |||
| 182 | $this->hasAdjustment($adjustment)->shouldReturn(false); |
||
| 183 | $this->addAdjustment($adjustment); |
||
| 184 | $this->hasAdjustment($adjustment)->shouldReturn(true); |
||
| 185 | } |
||
| 186 | |||
| 187 | function it_removes_adjustments_properly(AdjustmentInterface $adjustment) |
||
| 203 | |||
| 204 | View Code Duplication | function it_removes_adjustments_recursively_properly( |
|
| 205 | AdjustmentInterface $orderAdjustment, |
||
| 206 | OrderItemInterface $item |
||
| 207 | ) { |
||
| 208 | $this->addAdjustment($orderAdjustment); |
||
| 209 | $this->addItem($item); |
||
| 210 | |||
| 211 | $item->removeAdjustmentsRecursively(null)->shouldBeCalled(); |
||
| 212 | |||
| 213 | $this->removeAdjustmentsRecursively(); |
||
| 214 | |||
| 215 | $this->hasAdjustment($orderAdjustment)->shouldReturn(false); |
||
| 216 | } |
||
| 217 | |||
| 218 | function it_removes_adjustments_recursively_by_type_properly( |
||
| 219 | AdjustmentInterface $orderPromotionAdjustment, |
||
| 220 | AdjustmentInterface $orderTaxAdjustment, |
||
| 221 | OrderItemInterface $item |
||
| 222 | ) { |
||
| 223 | $orderPromotionAdjustment->getType()->willReturn('promotion'); |
||
| 224 | $orderPromotionAdjustment->isNeutral()->willReturn(true); |
||
| 225 | $orderPromotionAdjustment->setAdjustable($this)->shouldBeCalled(); |
||
| 226 | $orderPromotionAdjustment->isLocked()->willReturn(false); |
||
| 227 | |||
| 228 | $orderTaxAdjustment->getType()->willReturn('tax'); |
||
| 229 | $orderTaxAdjustment->isNeutral()->willReturn(true); |
||
| 230 | $orderTaxAdjustment->setAdjustable($this)->shouldBeCalled(); |
||
| 231 | $orderTaxAdjustment->isLocked()->willReturn(false); |
||
| 232 | |||
| 233 | $this->addAdjustment($orderPromotionAdjustment); |
||
| 234 | $this->addAdjustment($orderTaxAdjustment); |
||
| 235 | $this->addItem($item); |
||
| 236 | |||
| 237 | $item->removeAdjustmentsRecursively('tax')->shouldBeCalled(); |
||
| 238 | $orderTaxAdjustment->setAdjustable(null)->shouldBeCalled(); |
||
| 239 | |||
| 240 | $this->removeAdjustmentsRecursively('tax'); |
||
| 241 | |||
| 242 | $this->hasAdjustment($orderPromotionAdjustment)->shouldReturn(true); |
||
| 243 | $this->hasAdjustment($orderTaxAdjustment)->shouldReturn(false); |
||
| 244 | } |
||
| 245 | |||
| 246 | function it_returns_adjustments_recursively( |
||
| 271 | |||
| 272 | function it_has_adjustments_total_equal_to_0_by_default() |
||
| 276 | |||
| 277 | function it_calculates_correct_adjustments_total(AdjustmentInterface $adjustment1, AdjustmentInterface $adjustment2, AdjustmentInterface $adjustment3) |
||
| 297 | |||
| 298 | function it_returns_adjustments_total_recursively( |
||
| 320 | |||
| 321 | function it_has_total_equal_to_0_by_default() |
||
| 325 | |||
| 326 | View Code Duplication | function it_has_total_quantity(OrderItemInterface $orderItem1, OrderItemInterface $orderItem2) |
|
| 327 | { |
||
| 328 | $orderItem1->getQuantity()->willReturn(10); |
||
| 329 | $orderItem1->setOrder($this)->shouldBeCalled(); |
||
| 330 | $orderItem1->getTotal()->willReturn(500); |
||
| 331 | |||
| 332 | $orderItem2->getQuantity()->willReturn(30); |
||
| 333 | $orderItem2->setOrder($this)->shouldBeCalled(); |
||
| 334 | $orderItem2->equals($orderItem1)->willReturn(false); |
||
| 335 | $orderItem2->getTotal()->willReturn(1000); |
||
| 336 | |||
| 337 | $this->addItem($orderItem1); |
||
| 338 | $this->addItem($orderItem2); |
||
| 339 | |||
| 340 | $this->getTotalQuantity()->shouldReturn(40); |
||
| 341 | } |
||
| 342 | |||
| 343 | View Code Duplication | function it_calculates_correct_total(OrderItemInterface $item1, OrderItemInterface $item2, AdjustmentInterface $adjustment1, AdjustmentInterface $adjustment2) |
|
| 344 | { |
||
| 345 | $item1->getTotal()->willReturn(29999); |
||
| 346 | $item2->getTotal()->willReturn(45000); |
||
| 347 | |||
| 348 | $item1->equals(Argument::any())->willReturn(false); |
||
| 349 | $item2->equals(Argument::any())->willReturn(false); |
||
| 350 | |||
| 351 | $item1->setOrder($this)->shouldBeCalled(); |
||
| 352 | $item2->setOrder($this)->shouldBeCalled(); |
||
| 353 | |||
| 354 | $adjustment1->isNeutral()->willReturn(false); |
||
| 355 | $adjustment1->getAmount()->willReturn(10000); |
||
| 356 | $adjustment2->isNeutral()->willReturn(false); |
||
| 357 | $adjustment2->getAmount()->willReturn(-4999); |
||
| 358 | |||
| 359 | $adjustment1->setAdjustable($this)->shouldBeCalled(); |
||
| 360 | $adjustment2->setAdjustable($this)->shouldBeCalled(); |
||
| 361 | |||
| 362 | $this->addItem($item1); |
||
| 363 | $this->addItem($item2); |
||
| 364 | $this->addAdjustment($adjustment1); |
||
| 365 | $this->addAdjustment($adjustment2); |
||
| 366 | |||
| 367 | $this->getTotal()->shouldReturn(80000); |
||
| 368 | } |
||
| 369 | |||
| 370 | function it_calculates_correct_total_after_items_and_adjustments_changes( |
||
| 418 | |||
| 419 | View Code Duplication | function it_ignores_neutral_adjustments_when_calculating_total(OrderItemInterface $item1, OrderItemInterface $item2, AdjustmentInterface $adjustment1, AdjustmentInterface $adjustment2) |
|
| 420 | { |
||
| 421 | $item1->getTotal()->willReturn(29999); |
||
| 422 | $item2->getTotal()->willReturn(45000); |
||
| 423 | |||
| 424 | $item1->equals(Argument::any())->willReturn(false); |
||
| 425 | $item2->equals(Argument::any())->willReturn(false); |
||
| 426 | |||
| 427 | $item1->setOrder($this)->shouldBeCalled(); |
||
| 428 | $item2->setOrder($this)->shouldBeCalled(); |
||
| 429 | |||
| 430 | $adjustment1->isNeutral()->willReturn(true); |
||
| 431 | $adjustment1->getAmount()->willReturn(10000); |
||
| 432 | $adjustment2->isNeutral()->willReturn(false); |
||
| 433 | $adjustment2->getAmount()->willReturn(-4999); |
||
| 434 | |||
| 435 | $adjustment1->setAdjustable($this)->shouldBeCalled(); |
||
| 436 | $adjustment2->setAdjustable($this)->shouldBeCalled(); |
||
| 437 | |||
| 438 | $this->addItem($item1); |
||
| 439 | $this->addItem($item2); |
||
| 440 | $this->addAdjustment($adjustment1); |
||
| 441 | $this->addAdjustment($adjustment2); |
||
| 442 | |||
| 443 | $this->getTotal()->shouldReturn(70000); |
||
| 444 | } |
||
| 445 | |||
| 446 | View Code Duplication | function it_calculates_correct_total_when_adjustment_is_bigger_than_cost(OrderItemInterface $item, AdjustmentInterface $adjustment) |
|
| 447 | { |
||
| 448 | $item->getTotal()->willReturn(45000); |
||
| 449 | |||
| 450 | $item->equals(Argument::any())->willReturn(false); |
||
| 451 | |||
| 452 | $item->setOrder($this)->shouldBeCalled(); |
||
| 453 | |||
| 454 | $adjustment->isNeutral()->willReturn(false); |
||
| 455 | $adjustment->getAmount()->willReturn(-100000); |
||
| 456 | |||
| 457 | $adjustment->setAdjustable($this)->shouldBeCalled(); |
||
| 458 | |||
| 459 | $this->addItem($item); |
||
| 460 | $this->addAdjustment($adjustment); |
||
| 461 | |||
| 462 | $this->getTotal()->shouldReturn(0); |
||
| 463 | } |
||
| 464 | |||
| 465 | function it_initializes_creation_date_by_default() |
||
| 469 | |||
| 470 | function it_has_no_last_update_date_by_default() |
||
| 474 | |||
| 475 | function it_is_empty_by_default() |
||
| 480 | |||
| 481 | function it_should_be_able_to_clear_items(OrderItemInterface $item) |
||
| 489 | |||
| 490 | function it_has_notes() |
||
| 491 | { |
||
| 492 | $this->setNotes('something squishy'); |
||
| 493 | $this->getNotes()->shouldReturn('something squishy'); |
||
| 494 | } |
||
| 495 | } |
||
| 496 |
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.