为什么Java中继承多数是有害的

网络营销 2025-04-16 11:27www.168986.cn短视频营销

大多数优秀的设计师都倾向于避免使用实现继承(extends关系)。实际上,他们更倾向于使用接口(interfaces)来编写大约80%的代码。在“Java设计模式”这本书中,详细阐述了如何使用接口继承替代实现继承。那么,为什么设计师会这么做呢?

我们来谈谈为什么实现继承是有害的。当我们使用具体的类名时,我们实际上是在将自己固定在特定的实现上,这给底层的改变带来了不必要的困难。在敏捷编程方法中,设计应该允许更大的灵活性,以适应新的需求和变化。而实现继承往往会限制这种灵活性。这就是为什么许多设计师选择避免使用实现继承的原因。

相反,接口提供了一种更加灵活的方式来编程。让我们通过一个例子来说明这一点。假设你正在编写一个关于狼蚁网站SEO优化的代码。如果你使用实现继承,当你的需求发生变化时(比如需要更快的查询速度),你可能需要修改大量的代码,因为你在多个地方使用了具体的类(如LinkedList)。如果你使用接口来编写你的代码,你就可以更容易地适应这种变化。你可以简单地将LinkedList替换为HashSet或其他任何合适的类,而无需修改其他代码。这是因为你的代码依赖于接口而不是具体的类。这种灵活性是设计师选择使用接口的重要原因之一。

深入理解继承中的耦合与脆弱的基类问题

在软件设计中,耦合是一个关键的概念,它描述的是程序各部分之间的依赖关系。全局变量是强耦合的一个经典例证。当全局变量的类型发生改变时,所有使用该变量的函数都可能受到影响,需要进行检查、变更和重新测试。这种依赖关系可能导致维护成本增加,尤其是在多线程程序中。

作为设计师,我们的目标应该是努力最小化这些耦合关系。虽然无法完全消除耦合,但我们可以遵循面向对象的设计原则,如封装和最小化接口,来降低耦合度。例如,我们可以使用私有成员变量和私有方法来实现对象的封装,避免使用get/set函数来访问字段,以减少复杂性。

现在,让我们将耦合的概念应用到继承中。在基于extends的继承系统中,派生类与基类之间存在紧密的耦合,这种紧密耦合可能会导致“脆弱的基类问题”。当基类发生变化时,可能会影响到派生类的行为,即使这种变化看起来是安全的。一个简单的变化可能会导致整个程序的功能紊乱。

以一个简单的Stack类为例,这个类继承自ArrayList,并添加了一些自己的方法以实现栈的操作。当用户使用clear()方法清空堆栈时,由于基类的行为并未考虑到堆栈指针的状态,可能会导致对象处于未定义的状态。这种情况下,即使代码能够成功编译,也可能导致运行时错误。

为了解决这个脆弱的基类问题,一种方法是覆盖所有可能修改数组状态的ArrayList方法。但是这种方法有两个缺点。如果只是为了覆盖方法而覆盖基类,那么这个基类应该是一个接口而不是类。完全覆盖所有方法可能会增加工作量和维护成本。

我们需要更加谨慎地处理继承中的耦合问题。在设计和实现继承时,我们应该遵循面向对象的设计原则,确保基类的行为对于派生类来说是稳定和可预测的。我们也应该尽量减少对基类的修改,以避免可能的不稳定行为影响到派生类。对于必须进行的修改,我们应该进行全面测试和验证,以确保整个系统的稳定性和正确性。在编程的世界里,有一种重要的原则,那就是避免过度泛化。这就意味着我们不能让一种数据结构(比如一个堆栈)去适应所有的用途和功能需求。针对具体的场景和需求去定制设计堆栈功能是非常重要的。这就好比我们不能用一把锤子去解决所有的问题一样,那样不仅无法解决问题,反而可能造成更多的困扰。让我们深入一下这个重要的概念。

当我们遇到一个通用的堆栈类,里面包含一些可能永远不会被调用的方法,比如removeRange(),这是一个很大的问题。一个更好的做法是,对于这样的方法,我们应该让它们抛出异常,而不是仅仅让它们不存在或者尝试执行无效的操作。因为只有在运行时,我们才能真正知道哪些操作是无效的,编译器无法预测这些。通过抛出异常,我们可以更早地在运行时发现问题并解决它。

对于这个问题,一个更好的解决方案是使用封装而不是继承。让我们以一个改进的堆栈类为例:

这个新的堆栈类叫做Stack,它内部封装了一个ArrayList用于存储数据。这个类包含了push、pop和push_many等方法。这是一个很好的开始,但是如果我们想要跟踪堆栈的最大尺寸怎么办?我们可以创建一个新的类,叫做Monitorable_stack,它继承自Stack类并添加了一些额外的功能。这个新的类有两个变量:一个用于跟踪当前的堆栈尺寸,另一个用于跟踪最大的堆栈尺寸。

在Monitorable_stack类中,每当执行push操作时,都会检查当前的堆栈尺寸是否超过了最大尺寸。如果是,那么就更新最大尺寸。而当执行pop操作时,会减少当前的堆栈尺寸。还提供了一个方法用于获取到目前为止的最大尺寸。这个设计非常灵活,能够很好地适应各种需求。

特别值得一提的是,Monitorable_stack类中的push_many方法是通过调用push方法来实现的。这是一个非常好的设计选择,因为它简化了代码并确保了功能的正确性。即使是通过Stack的引用访问Monitorable_stack,也能够正确地更新最大尺寸。这样的设计使得代码既简洁又高效,同时确保了功能的完整性。这是一种真正考虑到实际需求的设计方式,值得我们学习和借鉴。这种设计不仅适用于编程中的数据结构问题,也适用于其他各种场景和需求的问题解决。

Copyright © 2016-2025 www.168986.cn 狼蚁网络 版权所有 Power by