【问题标题】:Better to extend a class or modify it directly?更好地扩展一个类或直接修改它?
【发布时间】:2011-02-13 10:47:33
【问题描述】:

所以我正在努力为 Java 中的数据结构创建可视化。我已经开始实现数据结构(二叉搜索树),但我需要向包含的节点类添加一些额外的功能。就约定和最佳实践而言,我应该创建一个具有此附加功能的节点的子类,还是应该只修改我拥有的并在那里记录它?

我的问题与here 的问题相似,但我有点想不通。

我知道这对我正在做的事情可能无关紧要,所以我把这个问题作为一个普遍的问题来问。

编辑:我可能应该更清楚。我的修改实际上并没有改变原始实现,只是添加了几个额外的字段(x 和 y 坐标加上一个布尔值来设置是否突出显示该节点)和访问/修改这些字段的函数。我正在使用的节点类也包含在 BST 实现中

从阅读您的答案看来,这两种情况似乎都有待商榷。我同意创建一个单独的类或接口通常是最好的做法。创建另一个类似乎会变得很棘手,因为您仍然需要一种从节点中提取数据的方法。我使用的 BST 实现是通用的,它本身在 Node 类或 BST 类中没有任何此类功能,只能返回数据,所以至少我必须添加它。

感谢您提供信息丰富的回复。

【问题讨论】:

标签: java class extend


【解决方案1】:

要回答的问题是,当您不可视化数据结构时,“基本功能”是否有用,甚至令人讨厌?

您甚至可能根本不想扩展该类。没有更多细节,在我看来你有一个有效的数据结构。您可以创建一个知道如何对其进行可视化的新类。

也就是说,你有一个数据结构和另一个知道如何可视化数据结构的类,而不是知道如何可视化自身的数据结构。哎呀-您可能会发现这演变成另一个完整的类层次结构,因为您可能需要可视化队列、堆栈等等等。与您的二叉搜索树无关。

【讨论】:

  • ++ 完美。每个班级都应该做好一件事。存储数据和可视化数据是两件事。
【解决方案2】:

既然您问的是笼统的问题,下面是简短的回答:这确实取决于具体情况

首先,假设子类与其父类具有“IS-A”关系。如果您不能说您的新子类是原始类的特定类型,那么您问错了问题,应该创建一个新的、不相关的类。

  • 如果新代码与类的核心目的密切相关,并且适用于类的所有成员(例如所有 BST),则修改可能会更好。高cohesion 不错。
  • 如果您的新代码与类的核心目的相关,但仅与该类型的 一些 对象有关(例如仅平衡的 BST),则子类化可能是可行的方法.
  • 根据您要更改的内容、您的代码使用了多少地方、有多少不同的人/组织正在使用它等等,您的更改可能会导致其他代码出现意外行为,因此您应该在修改之前三思而后行现有代码。这并不意味着自动对常用事物进行子类化;由于上述原因,这通常是错误的。

在您的具体情况下,我同意 n8wrl;由于可视化与数据结构无关,实现一个完全独立的Visualizable 接口可能比创建一个DrawableBSTNode 子类更好。

【讨论】:

    【解决方案3】:

    我想说,在向现有实现添加功能的一般情况下,您应该扩展现有实现而不是修改它。

    这是我的推理。如果该节点在除二叉搜索树实现之外的任何地方使用,那么当您修改它时,您需要找到它用于确保这些地方没有与您的修改冲突的任何地方。虽然只是以新方法的形式添加功能通常不会导致问题,但它可能会导致问题。你永远不知道对象是如何被使用的。

    其次,即使它只在二叉搜索树中使用,您仍然需要确保 BST 的实现能够很好​​地配合您的修改。

    最后,如果你扩展它,你不必担心第一点和第二点。您还可以获得额外的好处,即您的修改始终与原始实现分开。这样可以更轻松地跟踪您所做的事情并对其发表评论。

    【讨论】:

    • 我不确定扩展是否适用于所有情况。比如说,如果一个树基类想要在一个不受“覆盖”保护的区域中创建一个新对象,那么你就不能使用你的类返回你自己的对象,那么你基本上是被搞砸了。
    • @Chris 非常正确。这就是发挥创造力的时候了;)
    【解决方案4】:

    没有简单的答案,知道何时以及如何添加功能是您必须随着时间的推移而学习的东西。

    添加到基类似乎是简单的解决方案,但它会污染您的基类。如果这是一个类,您可以合理地期望另一个程序(甚至您的程序的一部分)使用您添加的功能在您的类职责的上下文中是否有意义?如果不是这样,这可能是一个糟糕的举动。您是否添加了将基类链接到您的特定用途的依赖项?因为如果你是这样的话,那就是把代码重用扔到窗外了。

    继承是许多工程师倾向于采用的解决方案,而且是一条诱人的路线。但随着我成长为一名工程师,我很少使用它。继承只能用于真正的 is-a 关系,并且您需要尊重behavioral subtyping 否则你以后会后悔的。由于 Java 只允许单一继承,这意味着您只能进行一次子类型化。

    组合(尤其是接口)通常是一个更好的主意。通常看起来像是一种关系的关系实际上是一种拥有的关系。或者有时你真正需要的只是一个帮助类,它有许多以你的原始类作为参数的函数。

    但是组合存在一个问题,希望将这些对象存储在树中。这里的解决方案是接口。你不想要一棵存储节点的树。您希望对象具有可以为您提供节点的接口。

    public interface HasNode {
        public Node getNode();
    }
    

    您的节点类是一个 HasNode,getNode 只是返回这个。您的 NodeVisualizer 类也是 HasNode,现在您也可以在树中存储 NodeVisualizer。当然现在你有另一个问题,你的树可能包含 NodeVisualizers 和 Nodes,那不是很好。另外,当您从树函数中获取 HasNode 时,您必须将它们强制转换为正确的实例,这很丑陋。您需要为此使用模板,但这是另一个答案。

    【讨论】:

      【解决方案5】:

      混合逻辑上独立的功能会导致混乱。子类化是一种非常特殊的关系,经常被过度使用。子类化适用于 Is-a-Kind 关系。

      如果您想可视化某些东西,为什么不为此创建一个完全独立的类呢?您可以简单地将您的 Node 对象传递给它。 (或者更好的是,使用接口。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-06
        相关资源
        最近更新 更多