Sales Tax Problem With Design Pattern

Problem Statement

Basic sales tax is applicable at a rate of 10% on all goods, except books, food, and medical products that are exempt. Import duty is an additional sales tax applicable on all imported goods at a rate of 5%, with no exemptions.

When I purchase items I receive a receipt which lists the name of all the items and their price (including tax), finishing with the total cost of the items, and the total amounts of sales taxes paid. The rounding rules for sales tax are that for a tax rate of n%, a shelf price of p contains (np/100 rounded up to the nearest 0.05) amount of sales tax.

Write an application that prints out the receipt details for these shopping baskets.
 
Input
  • 1 book at 12.49
  • 1 music CD at 14.99
  • 1 chocolate bar at 0.85 

Discussion

Let's say the user buys some products like apples, books or coolers. So, here the design should not be done so that cooler or apple will tell how much tax the user is going to pay. Government policy is responsible for telling the user how much tax he or she is going to pay on it. Object should be responsible for its own concern, and not anyone else's. The concern of a cooler machine is blowing cool air, not charging the tax. If tax law or tax percentage changes, user doesn't change the cooler machine, user needs to change the policy object.
 
Now, before the application design I will bring up all nouns used in the problem statements and relationship between them. Below are the points,
  1. Sales Tax is a kind of Sales Tax
  2. Import duty is also kind of Sales Tax
  3. Sales Tax has a rate which is a Decimal
  4. Books are a kind of item
  5. Food is a kind of item
  6. Medicines are a kind of item.
  7. Items may be imported
  8. Sales Tax will have some policy which will define that whether sales tax is applicable or not on a given item.
  9. An item has Sales Price in the form of Decimal.
  10. A Receipt containing list of items, price and their taxes
  11. Total Price
  12. Total Tax
Here, we have relationship between all nouns and can start design. 
Two basic Classes
  1. Item - It will be an abstract class and Book will inherit from it.
  2. SalesTax - It will be an abstract class and BasicSalesTax class will inherit from it.
In design this problem statement can be modeled as Shopping store and it will have below steps:
  1. A Store will have different kinds of product.
  2. User can put item in cart and purchase it.
  3. Payment counter will calculate Sales Tax as per eligibility of item and price. It will also calculate total price. It will generate bill by containing all details.

Design Pattern Approach

I will use two different kinds of design pattern for the design of Sales Tax Problem. Below are the Design patterns:
 
Abstract Factory Design Pattern

The Product is an Abstract Super Class and different  kinds of items like Book Cooler, Medicine, Food, etc. will inherit from the Product class. It will give the facility to extend the new Product type in future. 
 
Here, main reason to separate class for each product type is that other attributes can be added in the future by just extending the Product class.
 
So, below classes will be involved,
  1. Abstract Factory - ProductFactory
  2. Concrete Factory - BookProductFactory, FoodProductFactory, MedicineProductFactory
  3. Abstract Product - Product
  4. Concrete Product - BookProduct, FoodProduct, MedicineProduct
  5. Client - StoreShelf
Advantages
  1. If new Product will be added, then there will be no need to change the existing client code.
Strategy Design Pattern

Tax Calculation will be designed by using Strategy Pattern.
 
So, below classes will be involved,
  1. Strategy - ITaxCalculator
  2. Concrete Strategy - LocalTaxCalculator
  3. Context - Biller
Advantages
  1. We can add new algorithm for Tax Calculation in the feature easily and algorithm can be selected at run time.
  2. The actual creation of a TaxCalculator will be delegated to Factory method.

Sequence Diagram of Sales Tax Problem


Implementation

I will add a short code example. For more details find the attached code.

A Short code of Abstract Factory,
 

  1. using SalesTax.ProductFactories;  
  2. using System;  
  3.   
  4. namespace SalesTax.Products  
  5. {  
  6.     public abstract class Product  
  7.     {  
  8.         protected string Name { getset; }  
  9.   
  10.         private double _price;  
  11.         public double Price  
  12.         {  
  13.             set { _price = value; }  
  14.             get { return _price * Quantity; }  
  15.         }  
  16.   
  17.         public bool Imported { getset; }  
  18.   
  19.         public int Quantity { getset; }  
  20.   
  21.         public double TaxedCost { getset; }  
  22.   
  23.         public Product()  
  24.         {  
  25.             this.Name = string.Empty;  
  26.             this.Price = 0.0;  
  27.             this.Imported = false;  
  28.             this.Quantity = 0;  
  29.             this.TaxedCost = 0.0;  
  30.         }  
  31.   
  32.         public Product(String name, double price, bool imported, int quantity)  
  33.         {  
  34.             this.Name = name;  
  35.             this.Price = price;  
  36.             this.Imported = imported;  
  37.             this.Quantity = quantity;  
  38.         }  
  39.   
  40.         public override string ToString()  
  41.         {  
  42.             return (Quantity + " " + ImportedToString(Imported) + " " + Name + " : " + TaxedCost);  
  43.         }  
  44.   
  45.         public String ImportedToString(bool imported)  
  46.         {  
  47.             if (!imported)  
  48.                 return string.Empty;  
  49.             else  
  50.                 return "imported";  
  51.         }  
  52.   
  53.         public abstract ProductFactory GetFactory();  
  54.   
  55.         public abstract double GetTaxValue(String country);  
  56.     }  
  57. }  
  1. using SalesTax.ProductFactories;  
  2. using SalesTax.TaxCalculations;  
  3. using System;  
  4.   
  5. namespace SalesTax.Products  
  6. {  
  7.     public class BookProduct : Product  
  8.     {  
  9.   
  10.         public BookProduct()  
  11.             : base()  
  12.         {  
  13.         }  
  14.   
  15.         public BookProduct(String name, double price, bool imported, int quantity)  
  16.             : base(name, price, imported, quantity)  
  17.         {  
  18.   
  19.         }  
  20.   
  21.         public override ProductFactory GetFactory()  
  22.         {  
  23.             return new BookProductFactory();  
  24.         }  
  25.   
  26.         public override double GetTaxValue(string country)  
  27.         {  
  28.             if (country == "Local")  
  29.                 return LocalTaxValues.BOOK_TAX;  
  30.             else  
  31.                 return 0;  
  32.         }  
  33.     }  
  34. }  
  1. using SalesTax.Products;  
  2. using System;  
  3.   
  4. namespace SalesTax.ProductFactories  
  5. {  
  6.     public abstract class ProductFactory  
  7.     {  
  8.         public abstract Product CeateProduct(String name, double price, bool imported, int quantity);  
  9.     }  
  10. }  
  1. using System;  
  2. using System.Collections.Generic;  
  3.   
  4. namespace SalesTax.ProductFactories  
  5. {  
  6.     public class BookProductFactory : ProductFactory  
  7.     {  
  8.         public override Products.Product CeateProduct(string name, double price, bool imported, int quantity)  
  9.         {  
  10.             return new Products.BookProduct(name, price, imported, quantity);  
  11.         }  
  12.     }  
  13. }  
Strategy Pattern Basic Code, 
  1. using System;  
  2.   
  3. namespace SalesTax.TaxCalculations  
  4. {  
  5.         double CalculateTax(double price, double tax, bool imported);  
  6.     }  
  7. }  
  1. using SalesTax.utils;  
  2. using System;  
  3.   
  4. namespace SalesTax.TaxCalculations  
  5. {  
  6.     /// <summary>  
  7.     /// It Calculates Total Tax Cost According to Local Region Specification.  
  8.     /// </summary>  
  9.     public class LocalTaxCalculator : ITaxCalculator  
  10.     {  
  11.         public double CalculateTax(double price, double localTax, bool imported)  
  12.         {  
  13.             double tax = price * localTax;  
  14.   
  15.             if (imported)  
  16.                 tax += (price * 0.5);  
  17.   
  18.             //rounds off to nearest 0.05;  
  19.             tax = TaxUtil.RoundOff(tax);  
  20.   
  21.             return tax;  
  22.         }  
  23.     }  
  24. }  
  1. using System;  
  2.   
  3. namespace SalesTax.TaxCalculations  
  4. {  
  5.     public class TaxCalculatorFactory  
  6.     {  
  7.         private Dictionary<String, ITaxCalculator> taxCalculators;  
  8.   
  9.         public TaxCalculatorFactory()  
  10.         {  
  11.             taxCalculators = new Dictionary<String, ITaxCalculator>();  
  12.             RegisterInFactory("Local"new LocalTaxCalculator());  
  13.         }  
  14.   
  15.         public void RegisterInFactory(string strategy, ITaxCalculator taxCalc)  
  16.         {  
  17.             taxCalculators.Add(strategy, taxCalc);  
  18.         }  
  19.   
  20.         public ITaxCalculator GetTaxCalculator(String strategy)  
  21.         {  
  22.             ITaxCalculator taxCalc = (ITaxCalculator)taxCalculators[strategy];  
  23.             return taxCalc;  
  24.         }  
  25.   
  26.         public int GetFactorySize()  
  27.         {  
  28.             return taxCalculators.Count;  
  29.         }  
  30.     }  
  31. }  
Implementation of this Sales Tax Problem is very large so I could not include here. I added Zipped project. Take reference from there. 

Conclusion

Sales Tax Problem teaches and makes us think about the design approach. So, here Abstract factory and Strategy Design Patterns have been applied. It is a very good problem statement to think and design applications by using design patterns.