I am working on an application that will help me improve my OO design skills. I have almost completed the assignment but feel that I need to make my application more extensible and need some inputs on how to achieve this.
The problem domain can be described in a nutshell as follows:
The problem domain is to design a shopping cart application. There are different categories for an item such as food, books, etc. The rate of tax for which an item is eligible depends upon the category of the item. There are well defined tax policies, e.g basic tax of 10 % is applicable for all items except items belonging to the category food, all imported items are subject to additional import duty tax of 5 %.
The area I want to focus on is the module responsible for deciding the eligibility of an item for a given tax policy. I have used ideas from other people and the internet to come up with the following design for the tax module :
TaxCriteria : defines the condition that must be satisfied for an item to eligible for a TaxPolicy. Concrete implementers implement the isEligible method and provide the business logic for determining whether an item is eligible for a policy or not eg. isEligible method in ImportedTaxCriteria will check if an item is imported or not.
TaxPolicy : has a TaxCriteria, the rate at which the tax is applicable, and the name for the tax policy. The relation between TaxPolicy and TaxCriteria is based on the specification pattern.
SalesPolicy : is made up of one or more TaxPolicy instances.
SalesEngine : SalesEngine has a SalesPolicy and a TaxCalculator. The applyPolicies method is passed an Order that contains a list of items. applyPolicies will iterate through each item in the order and check its eligibility against each TaxPolicy in the sales policy and calculate the tax using a TaxCalculator based on the applicable policies.
The new problem domain :
Every country has different sales policies defined. Import duty in the US could be 5 percent whereas import duty in China could be 3 %. To address this requirement, I was thinking of using a simple factory pattern. SalesPolicy will no longer be a class but an interface with a getTaxPolicies method that returns a list of TaxPolicy. USASalesPolicy and ChinaSalesPolicy will implement SalesPolicy and the getTaxPolicies method will return a list of TaxPolicy pertaining to the specific country respectively. Similarly, every country will have its own tax calculation adjustments. USATaxCalculator and ChinaTaxCalculator can take care of indepent implementations.
Finally, SalesEngine will be passed a SalesPolicyFactory and a TaxCalculatorFactory in the constructor instead of directly passing a SalesPolicy and a TaxCalcualtor. The applyPolicies method will be passed an additional parameter which will be a String representing the country. The applyPolciies method will pass the country to the factories in SalesEngine and use them to create appropriate SalesPolicy and TaxCalculator instances to be used for calculating the tax.
This is what my thought process has been so far. Am I doing justice to the well defined design principles an the factory pattern? How can I use abstract factory pattern such that SalesPolicy and TaxCalculator represent the families of related products? Is there a better way to solve the problem than the one I have used?
You can, if you wish, incorporate the Abstract Factory pattern to deal with the creation of TaxPolicy objects.
In your case that would involve:
AbstractTaxPolicyFactory
– an interface with abstract methods createItemCategoryTaxPolicy()
and createImportDutyTaxPolicy()
, that would create abstract tax policies ItemCategoryTaxPolicy
and ImportDutyTaxPolicy
(these would be your "families of related products")
USTaxPolicyFactory
and ChinaTaxPolicyFactory
– two concrete factories, each implementing those two methods to create concrete tax policies like ChinaImportDutyTaxPolicy
, etc.
ItemCategoryTaxPolicy
and ImportDutyTaxPolicy
– interfaces for the respective concrete tax policies
USItemCategoryTaxPolicy
, USImportDutyTaxPolicy
, ChinaItemCategoryTaxPolicy
, and ChinaImportDutyTaxPolicy
– the four concrete tax policies. For instance, at runtime the USImportDutyTaxPolicy
would be created by the USTaxPolicyFactory
, and would be of ImportDutyTaxPolicy
type.
SalesPolicy
would be your client that would use the abstract factory and abstract tax policies interfaces, e.g.:
AbstractTaxPolicyFactory absFactory = new ChinaTaxPolicyFactory();
// or use a static factory method:
// TaxPolicyFactoryCreator.createNewFactory(Country.CHINA);
ItemCategoryTaxPolicy itemCatTaxPolicy = absFactory.createItemCategoryTaxPolicy();
addTaxPolicy(itemCatTaxPolicy);
ImportDutyTaxPolicy importDutyTaxPolicy = absFactory.createImportDutyTaxPolicy();
addTaxPolicy(importDutyTaxPolicy);