关于何时使用继承以及何时不使用,存在大量讨论。在这种情况下,我不会使用继承来为您的项目建模。
为什么?因为在现实生活中,这些税收和关税规则在时间、空间(例如您所在的州)以及您所经营的公司类型方面经常发生变化。如果每次地方和联邦政府更改法规时您都必须更改类层次结构,那么您注定要失败-更新将更改您已经拥有实例的类-您的项目。
另一个考虑因素:如果您使用类来(有效地)将销售“标记”为具有税收和/或关税,您最终会得到一些没有标记接口的项目,而有些则具有多个 - 即使用多重继承。你需要四门课。添加一些额外的规则,你就有了一个类爆炸(考虑税/关税的数量)。
另一种实现是拥有一个属性,该属性是该项目所拥有的税收和关税的集合。所以你有两个类:
Tax 有许多全局实例 - SalesTax 和 ImportDuty。然后一个项目在列表中有零个、一个或两个 Tax 实例(并且该列表还暗示了税收的应用顺序)。
最后一种方法也更好地适应未来税收变化,包括增加新税
编辑隔夜我有一个改进的建议:
该图有两个“翅膀”:
- AbstractItem 层次结构,处理项目及其税收
- TaxOrDuty 层次结构和 TaxAssessor 中的算法。
AbstractItem 在 Item 上使用装饰器模式,将 Item 包装在税层中——每一层都由 TaxedItem 建模。最外层是包裹在所有适用税项中的物品,并且使用装饰器模式,外观和行为与物品一样,只是价格虚高。AbstractItem 有几个有用的方法:
- getPrice():返回商品的全价
- getAllTaxes():传入一个空列表,该方法在收集 TaxOrDuty 实例的装饰器上递归。这对于能够逐项列出装饰物品的税款很重要
- getUntaxedItem():遍历装饰直到找到它返回的项目。这允许从装饰的项目中轻松检索原始项目。
在税收方面,有一个 TaxOrDuty 类,它对一般税收的抽象行为进行建模。在这个简单的实现中,每种税都有一个统一税率,但您可以轻松地将这种行为下放到 FlatRateTax 类中。它的方法是:
- getRate():返回税率或关税率的抽象方法。这是在子类上实现的
- computeTax() 方法接受一个 Item 并根据 getRate() 计算它的税金
- isApplicable() 是一个抽象方法,如果税款适用于该项目,则返回 true。这是在 SalesTax(总是返回 true)和 ImportDuty(返回 Item.isImported)上实现的
TaxAssessor 是一个负责协调适用税收分配的类别。它包含所有 TaxOrDuty 实例的列表(在本例中只有一个 SalesTax 实例和一个 ImportDuty 实例)。它的方法 applyTaxes(Item) 遍历这个调用 isApplicable() 的集合。对于任何返回 true 的 TaxOrDuty,TaxAssessor 将 Item 包装在 TaxedItem 的新实例中(当然它引用了 TaxOrDuty):
AbstractItem applyTaxes(Item item) {
taxed = item;
for (TaxOrDuty td : taxes) {
if (td.isApplicable(item)) {
taxed = new TaxedItem(td, taxed);
return taxed;
因此,您的总体答案将类似于:
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>());
该模型的主要优点是:
- 新税是可插入的
- 税收评估和计算算法是税收的一部分,而不是在项目中,因此非常复杂的税收可以在自己的类中独立且正确地建模
- 责任是显而易见的(对我而言)并且界限分明