In this post, we will take a look at calculating coffee price with different add-ons.
We have coffee which is our base product, then we add different add-ons and each add-on can add a simple price on top of it. And finally leading to tax calculation giving us the total amount.
We can think of this as wrapping the base product (coffee) with each add-on as in following picture:
Thinking in terms of code, each of the layer can be thought of creating a new object and each of those layer takes the object creating before it. So, Creamer takes Coffee object, Sweetner takes Creamer object and so on. In order to achieve this, it makes sense they either derive a common class or implement a common interface.
Let's call the interface IProduct. Since, we have to calculate the price, let's add GetPrice() as well.
interface IProduct{
double GetPrice();
}
Now, I can create a Coffee class implementing IProduct
class Coffee : IProduct {
public double GetPrice() => 3;
}
What about the add ons? Each add-on should add a price on top of the base product which can be achieved by
class AddOn: IProduct {
private IProduct _addOn;
private double _price;
public AddOn(IProduct addOn, double price){
_addOn = addOn;
_price = price;
}
public double GetPrice() => _price + _addOn.GetPrice();
}
Since AddOn and Coffee both implement IProduct. We can achieve the "wrapping" as follows
var coffee = new Coffee();
var coffeeWithCreamer = new AddOn(coffee, .20);
var coffeeWithCreamerAndSweetener = new AddOn(coffeeWithCreamer, 1);
To get the total price I can simply call
Console.WriteLine(coffeeWithCreamerAndSweetener.GetPrice());
// prints 4.2
How about tax? The calculation of tax is not the same as AddOn. In case of tax, we pass percent rather than price. So we create a new class for it also implementing IProduct
class TaxDecorator: IProduct {
private IProduct _addOn;
private double _percent;
public TaxDecorator(IProduct addOn, double percent){
_addOn = addOn;
_percent = percent;
}
public double GetPrice() {
var total = _addOn.GetPrice();
return total + ((total * _percent) / 100);
}
}
I specifically use the word "Decorator" in the above class name. This pattern is famously known as the Decorator Pattern. We "decorate" or "wrap" an object with another without changing any behavior of those objects.
So, finally I can add different price on top of our base product, basically decorating it with multiple add-ons, and are able to get the total price at the end. Much more cleaner and readable code than what we have.
var coffee = new Coffee();
var coffeeWithCreamer = new AddOn(coffee, .20);
var coffeeWithCreamerAndSweetener = new AddOn(coffeeWithCreamer, 1);
var totalWithTax = new TaxDecorator(coffeeWithCreamerAndSweetener, 8);
Console.WriteLine(totalWithTax.GetPrice());
// prints 4.536