扩展 class 只是为了使用它的方法和属性

Extending class just for the sake of using it's methods and attributes

在网上找到的很多关于Java extends的例子中,都使用了具有一定“逻辑联系”的classes。例如,banana 扩展了 fruitStudent 扩展了 Person

用另一个 class 扩展一个 class 只是为了继承方法和属性是一种好习惯吗,即使两个 class 都没有显示像这样的“连接”在上面的例子中?

例如,class UserManagementService 扩展了 class DatabaseConnectionService 这样 UserManagementService 可以通过调用 [=17= 方法简单地连接到数据库]connect() 而不是实例化 DatabaseConnectionService 并调用 databaseConnectionService.connect().

是的,这显然很糟糕;那是因为你的类型层次结构必然是 public API。如果您的 API 公开了 UserManagementService,我可以将其用作 DatabaseConnectionService 对象。这意味着您选择让 UMS 扩展 DCS 是 锁定在 - 如果您更改它,那么使用您的 UMS 的任何代码都可能会失败。您可以尝试在文档中解决这个问题:

/**
 * Lets you query information about, and perform operations on,
 * the storage of users allowed on this service.
 *
 * IMPORTANT NOTE: Even though this class extends DatabaseConnectionService,
 * this is <em>not guaranteed</em> by this implementation,
 * so do not rely on this!
 */
public class UserManagementService extends DatabaseConnectionService {
...
}

但是您肯定可以看到这不是最理想的并且 'ugly'(难以维护 - 您无法测试您无法控制的其他代码实际上注意到了您的警告)。

它也适用于相反的情况:如果 DCS 添加了一些对 UMS 没有意义的东西,那么你的 UMS 就会突然崩溃,并暴露出毫无意义的疯狂东西,并引起很多问题或更糟。

将此与声明 Student 是某种 Person 进行对比:这本质上是正确的;这不仅仅是一个方便的实现细节。如果整个国家/地区都获得了某种服务编号功能,并且您扩展了 Person class 来支持它,那么学生突然之间也获得了这种服务编号功能。但这很好:毕竟学生 人。

那么,你是如何解决的?

简单。不要扩展 DCS。创建一个 DCS 类型的字段,如果 DCS 有一堆您希望 UMS 也有的方法,请将它们写出来。他们的实现可以是非常简单的一行代码:

public int count() {
    return dataConService.count();
}
// and a lot more of this, if really needed.

对这种逻辑的普遍反驳:但我控制了一切,我预计不会添加新功能!

嗯,好吧,但要明白 single-person 一次性项目是一个非常糟糕的谈话基础 'style guides and code cleanliness' - 只有你,周末闲逛,随心所欲地写,你'会好的。

当一个由 50 名程序员组成的团队编写的应用程序要生存并在业务中运行十多年时,风格指南和编码方法变得有用,程序员离开,新程序员加入团队,5 年后项目启动了,你想不到的功能因为客户的需求需要添加

考虑到这一点,请理解代码库变得庞大,安全地更改内容和培训新程序员以熟悉它将会非常困难。使事情变得更容易的一种非常有用的方法是积极地将事物模块化:任何时候你都可以在白板上画出整个源代码库(这将是巨大的),然后在它的一小部分周围画一个小圆圈然后:东西可以自己理解,自己测试,并在没有完全理解所有其他源代码的情况下继续开发——那是 good。这就是你想要的。

“UMS 目前扩展了 DCS 但不依赖于它”正是那种让绘制那个小圆圈变得更加复杂的事情,这就是为什么这样做不是一个好主意。