if (item.isTaxable()) {
item.setCost(item.getCost *= TAX_RATE);
}
The root of the problem is that you only have one kind of Item.
public class Item {
private boolean taxable;
public boolean isTaxable() {
return taxable;
}
...
}
where two types are needed:
public interface Item {
double getTax();
}
public class TaxableItem implements Item {
public double getTax() {
return cost * TAX_RATE;
}
}
public class NontaxableItem implements Item {
public double getTax() {
return 0;
}
}
You also have a problem with how the object is created:
Item item = new Item(true);
Say you are implementing a shopping cart, and as the user adds items to the cart, the correct type of item is added. Then at the end, you can calculate tax for the taxable items because the cart already contains the right kind of item.
cart.addItem(ItemFactory.createItem(itemId));
Now there are no ifs in the domain code:
for (Item item : cart.items()) {
tax += item.getTax();
}
The moral: a lot of ifs and their accompanying headaches go away when you take the effort to create the right object.
2 comments:
I really like the technique of returning a zero/empty valued constant. Not only can it simplify the higher-level logic surrounding your data structures as you demonstrate, but it is a good way to slim down the memory use of your objects by coding to the most common use case.
For example Sun's LogRecord has fields describing calling class and method, parameters, and the specific level (when the normal use case is "info"). So I would reduce the record to only message, threadId, and millis time. Other fields can be made use of by subclassing.
Good post, Curtis. Good rule of thumb for me to remember!
DR
Post a Comment