There are huge discussions on when to used inheritance and when not to. In this case, I would not use inheritance to model your items.
Why? Because in real life these tax and duty rules change frequently both in time, space (eg which state you're in) and also by the type of company you're running. If you have to change the class hierarchy each time local and federal governments change their regulations, you're doomed - the update will change the classes of which you already have instances - your items.
Another consideration: if you use classes to (effectively) 'tag' a sale as having tax and/or duty, you will end up with some items having no tagging interfaces, and some with multiple - i.e. using multiple inheritance. You need four classes. Add in some additional rules, and you have a class explosion (factorial the number of taxes/duties to consider).
An alternate implementation is to have a property that is the collection of taxes and duties the item has. So you have two classes,:
Item
Tax
- String: name
- Double: percent
Tax has a number of global instances - SalesTax and ImportDuty. Then an item has zero, one or two Tax instances in the list (and the list also implies order that the taxes are applied).
This last approach is also much better future-proofed for tax changes, including the addition of new taxes
EDIT Overnight I have an improved suggestion:

There are two 'wings' to the diagram:
- AbstractItem hierarchy, handling the Item and its taxes
- TaxOrDuty hierarchy and an algorithm in TaxAssessor.
The AbstractItem uses the decorator pattern on an Item, wrapping the Item in layers of taxes - each layer modelled by TaxedItem. The outmost layer is an Item wrapped in all applicable taxes, and, using the decorator pattern, looks and behaves as an Item would, except for an inflated price. The AbstractItem has several useful methods:
- getPrice(): Returns the full price of the item
- getAllTaxes(): Pass in an empty list, and the method recurses over the decorators collecting the TaxOrDuty instances. This is important to be able to itemize the taxes on a decorated Item
- getUntaxedItem(): Recurses through the decorations until it finds the Item, which it returns. This allows easy retrieval of the raw Item from a decorated Item.
On the taxes side, there is a class TaxOrDuty that models the abstract behaviour of taxes in general. In this naive implementation, every tax has a flat rate, but you could easily push that behaviour down into a FlatRateTax class. Its methods are:
- getRate(): Abstract method that returns the tax or duties rate. This is implemented on the subclasses
- computeTax() method takes an Item and computes the taxes for it based on getRate()
- isApplicable() is an abstract method that returns true if the tax is applicable for the Item. This is implemented on SalesTax (always returns true) and ImportDuty (returns Item.isImported)
The TaxAssessor is a class responsible for co-ordinating the assignment of applicable taxes. It contains a list of all TaxOrDuty instances (in this case just one instance of SalesTax and one of ImportDuty). Its method applyTaxes(Item) iterates over this collection calling isApplicable(). For any TaxOrDuty that returns true, the TaxAssessor wraps the Item in a new instance of TaxedItem (which of course references the TaxOrDuty):
AbstractItem applyTaxes(Item item) {
taxed = item;
for (TaxOrDuty td : taxes) {
if (td.isApplicable(item)) {
taxed = new TaxedItem(td, taxed);
return taxed;
So your overall answer will be something like:
Item item = new Item("Book", 10.0, false); // New $10 book, not imported
AbstractItem withTaxes = taxAssessor.applyTaxes(item);
double taxedPrice = withTaxes.getPrice();
List<TaxOrDuty> applicableTaxes = withTaxes.getAllTaxes(new List<TaxOrDuty>());
The major advantages of this model are:
- New taxes are pluggable
- The tax assessment and computation algorithms are part of the Tax rather than in Item, so very complex taxes can be modelled independently and correctly in their own class
- Responsibilities are obvious (to me) and clearly demarcated
getTax()which return 10% for SaleTaxItem and 10%+5% for items eligable for import duty (overriding perhaps?)? I don't think boolean is a good scalable choice: imagine one day we've introduced a new tax: do we need to introduce new boolean? do we need to encode it in int?