6

考虑以下:

  public abstract class Item {
     String name;
     String description;
     //concrete getters and setters follow
  }

   public class InventoryItem extends Item {
     //empty subclass of Item 
   }

  public class CartItem extends Item {
     int quantity;
     int tax;
     //getters and setters for quantity and tax follow
  }

InventoryItem 表示可供出售的商品,而 CartItem 表示添加到购物车中的商品,因此它具有额外的属性,例如数量和税金。在这种情况下可以有一个抽象类 Item 的空子类吗?

选项 2:我们可以有一个空的 Item 接口。InventoryItem 将实现 Item 并定义名称和描述属性,并具有 getter 和 setter。CartItem 将从 InventoryItem 扩展,并将数量和税收定义为属性,并具有 getter 和 setter。

选项3:有一个Item界面会更好吗?InventoryItem 将实现 Item。然后我们可以有一个 CartItem 类,它“有一个”项目和两个属性,即税和数量

4

2 回答 2

5

我认为这种设计没有任何问题:它清楚地指定Item为基础,和InventoryItem/CartItem作为可实例化的类。我将重命名ItemAbstractItem(Java 类库执行此操作)以强调该类的预期用途将用作其他类的基础这一事实。

存在 C++ 特定的问题,例如通过基指针赋值,这使得将所有“非叶”类抽象化是非常可取的。尽管该规则并未从字面上翻译成 Java,但我认为将非叶类抽象化仍然是一个好主意,即使该类是自给自足的。这有助于您在用于直接实例化的类和用于扩展的类之间进行显式分离,这有利于不熟悉您的代码库的人进行维护。

将规则更进一步,在 Java 中创建所有叶类是一种常见的做法final。这是一个很好的防御规则,尤其是当你的类是不可变的时。

编辑:就购物车中的建模项目而言,继承不是最佳选择。事实上,我认为这样做是错误的。我会CartItem自己做一个Item,而不是扩展它,因为该项目不会通过放置在购物车中而成为另一种实体。这是我认为应该建模的方式:

public class Item {
    String name;
    String description;
    ...
}

public class CartItem {
   Item item;
   int quantity;
   int tax;
   ...
}
于 2012-08-02T18:17:01.273 回答
1

让我提供一个稍微长一点的建议或意见,这可能会提供一些指导。

在设计 is-a 层次结构时,我倾向于仅通过多态性来区分行为。一般来说,我倾向于避免仅通过数据区分对象。多年来,我什至停止为类似结构的“数据对象”创建继承层次结构,仅根据它们所持有的变量。

我花费最多的时间是在方法方面定义抽象类的通用接口,然后让子类都适用于相同的接口,但具有不同的行为。(里氏替换原则)

在这种情况下,我会考虑一个通用接口/抽象 Item 类,它具有 Item 实现的层次结构,它们在库存、计算税收等方面有所不同。我可以有一个方法 .isInCatalog() 来告诉我 item 在哪里或什么如果我必须以不同的方式处理它,这是它的本质,但我仍然会尝试将大多数项目类型特定逻辑封装到其多态方法的实现中。

尽管这些类型的计算实际上通常在数据库上的批处理/聚合操作中完成,而不是在单个对象中完成,但我会在抽象级别上使用 .getTax()、.getQuantity(),然后拥有具有不同行为的子类. (考虑将 NullObject 重构为无所事事以避免处理空值)

这篇可靠的博客文章中概述了有助于这种设计的一些原则:

http://codebork.com/2009/02/18/solid-principles-ood.html

于 2012-08-02T19:26:59.650 回答