6

I have reading about SOLID principles last week and decided to try to solve real world problem by using them. So I got the problem of real world ecommerce site managing totals.

Problem Statement: We have to calculate some totals like subtotal, taxes, vouchers, coupon, total on a cart products.

So I have decided to make TotalManager which returns total_data and total to the rest of the program.

// total data variable looks like
array('key'=>value)

e.g. array('subtotal'=>100,'total'=>150,'tax'=>50)

    class TotalManager{
        private $total_data;
        public function get_total_data()
        {
            $totals = array('subtotal','total','taxes')         
            // foreach totals array gets each object check its instance of interface itotal and called it's get method.
        }        
    }

    Interface Itotal
    {
        public function get_total_data();
        public function get_total();
    }    

    class subtotal implements Itotal
    {
        public function get_total_data()
        {
            // it needs cart object
            // get the cart product calculate totals and return total_data and total 
        }
        public function get_total()
        {
            return the subtotal in double/float
        }
    }

    class total implements Itotal
    {
        public function get_total_data()
        {
            // get the other totals object  and calls it get_total method
            // calulate sum of all totals and return 
        }

        public function get_total()
        {
            // calculate total and return back;
        }
    }

But I am facing problems in this model:

For dependency inversion principle I cannot directly create the object of cart or subtotal in subtotal, Total class respectively I have to pass them from controller at total manager.

When I think about that whenever I am adding the class I have to change the Total manager for adding the new method so it's violating open/closed principle. How to solve this in OOD?

  • As a side note "Clean Code", the book that advocates the SOLID principles, also frowns upon using the word "manager" in your class names. – Songo Feb 8 '16 at 11:24
1

I see you use php, try to stick to the psr coding styles: psr-1 and psr-2.


Look into the decorator pattern. this is a pattern commonly used in this kind of situations.


You probably have some kind of LineItem class that is used to represent items in your cart. They probably look something like this:

interface LineItem
{
    public function getPrice();
}

With then an implementation for your product:

class ProductLineItem implements LineItem
{
    public function __construct(Product $product, $quantity) {
        $this->product = $product;
        $this->quantity = $quantity;
    }

    public function getPrice()
    {
        return $this->quantity * $this->product->getPrice();
    }
}

Now it is time to create our abstract decorator:

abstract class LineItemDecorator implements LineItem
{
    public function __construct(LineItem $item) {
        $this->item = $item;
    }

    public function getPrice()
    {
        return $this->item->getPrice(); //delegation
    }
}

this abstract class is not needed. I just tend to use it to delegate everything to the base object only in one place. Especially useful if multiple methods exist.

Note also how my Decorator implements the same interface!

And then now it's time for our Discounts:

class DiscountLineITemDecorator extends LineItemDecorator 
{
    public function getPrice()
    {
        return $this->item->getPrice() * 0.75;
    }
}

We would then procede to use it:

$lineItem = new ProductLineItem($theProduct, 2);
$lineItemWithDiscount= new DiscountLineITemDecorator($lineItem);
//lets add it to our cart
$cart->addLineItem($lineItemWithDiscount);

Our cart then probably also has a getPrice() method. Probably it will look something like this:

foreach ($this->lineITems as $lineItem) {
    $price += $lineItem->getPrice();
}
return $price;

So what about discount on an entire cart?

$cartWithDiscount = new DiscountCartDecorator($cart);

and boom

$cartWithDiscount->getPrice();

All code used here serves as an example. The naming can be improved a lot, so feel free to edit!

  • I am just thinking in the same way as decorator pattern works but i doesn't know it's called as a decorator pattern ie. passing the main totalmanager object to every subtotal class to achieve all totals. – siddhesh Jul 14 '15 at 16:17
  • but is it good to pass whole object to everyone who doesn't need it? I mean to say sub-total class needs cart but total class doesn't need it. – siddhesh Jul 14 '15 at 16:19
  • Added some examples – Pinoniq Jul 15 '15 at 13:14
  • where are you defining class 'PriceDecorator' which is extended by 'DiscountLineITemDecorator' – siddhesh Jul 16 '15 at 12:58
  • Not, I called it PriceDecorator, but I changed it to LineItemDecorator when copy-pasting to the answer. Edited the answer – Pinoniq Jul 17 '15 at 8:31
-2

The Open/Closed principle allows you to add new methods. You won't be able to alter the interface to existing methods.

It may be simpler to have a class that takes a cart and returns/recalculates the totals. Try to avoid using this class to maintain state between calls.

  • will you please explain me about 'Try to avoid using this class to maintain state between calls' And please help how was the design should I change it or not. – siddhesh Jul 12 '15 at 11:50
  • Are you going to say that I should only keep totalManager class and getTotal,getSubtotal will be method of that. – siddhesh Jul 12 '15 at 14:20
  • and sir it's also going to break the SRP in solid because class now can change when the any of total methods change it's behaviour – siddhesh Jul 13 '15 at 13:42
  • @siddhesh It would maintain SRP. It would be a container for all totals, and would break SRP if you moved a total elsewhere. Classes are expected to change as requirements change. It would be possible to inject a list of sub-totalling classes. – BillThor Jul 14 '15 at 1:43
  • sir will you please explain bit more? I m unable to get your point. @BillThor – siddhesh Jul 14 '15 at 7:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.