1

I'm currently implementing a project and have a question regarding multiple classes that implement the same interface.

So, right now I have multiple discount types that might be applied to an Order. So, I created a DiscountInterface that has the check() method, that checks if the discount is applicable and the apply() method that applies the discount.

So far, so good. Then I implemented the first discount type in a class that implements the DiscountInterface and has the logic for checking and applying this particular discount.

In my controller, I inject the DiscountInterface. When an Order is received I call both the check() and apply() methods and everything is running perfectly.

My question is the following. I to implement a second type of discount. Following my implementation, I will have to create a new class that implements DiscountInterface. But when it's time to call it on the controller how should it be done. Since I have to different classes, with the same methods.

The following code works if I have one class implementing these methods, but what happens if I have two classes implementing them?

public function discount(
    Request $request, 
    DiscountInterface $discount, 
    CustomerRepository $customer, 
    ProductRepository $product, 
    ValidatorInterface $validator,
    OrderServiceInterface $orderService
)
{
    $data = json_decode($request->getContent(), true);
    $order = $orderService->convertDataToOrder($data, $customer, $product, $validator);

    if($discount->check($order, $customer)){
        $order = $discount->apply($order);
    }
4
  • The code will continue to work, it'll take any class that satisfies the "DiscountInterface", so even when you have 2 or 300 classes, you can throw them into that method as they're completely interchangeable. Commented Oct 25, 2018 at 10:07
  • Where is your controller for us to look at? Commented Oct 25, 2018 at 10:07
  • do you need to check against ALL discount types and figure out which ones apply? Commented Oct 25, 2018 at 10:08
  • Yes, I need to check all the discount types. Commented Oct 25, 2018 at 10:16

1 Answer 1

2

If you need to check against all discount types, instead of passing the implementing class, why not pass a DiscountFactory instead?

<?php

use My\Discount\CrapDiscount;
use My\Discount\AwesomeDiscount;

class DiscountFactory
{
    /** @var DiscountInterface[] */
    private $discounts;

    public function __construct()
    {
        $this->discounts = [
            new CrapDiscount(),
            new AwesomeDiscount(),
        ];
    }

    public function getDiscounts(): array
    {
        return $this->discounts;
    }
}

Then your code could look something like this:

public function discount(
    Request $request, 
    DiscountFactory $discountFactory, 
    CustomerRepository $customer, 
    ProductRepository $product, 
    ValidatorInterface $validator,
    OrderServiceInterface $orderService
)
{
    $data = json_decode($request->getContent(), true);
    $order = $orderService->convertDataToOrder($data, $customer, $product, $validator);

    foreach ($discountFactory->getDiscounts() as $discount) {
        if($discount->check($order, $customer)){
            $order = $discount->apply($order);
        }
    }
    // etc
Sign up to request clarification or add additional context in comments.

4 Comments

Sounds like a plan! Thanks for the idea!
@CláudioRibeiro Note that you might have to add checks to validate the order of the discounts; discounting an amount and then subtracting a percentage is not the same as doing it the other way around.
Cheers! If it works can you mark this correct please? :-D
@Claudio I moved it into the constructor

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.