【问题标题】:Why do so many foss libraries make their classes public and not final?为什么这么多 foss 库将他们的课程公开而不是最终的?
【发布时间】:2013-05-04 17:49:02
【问题描述】:

在你用一个明显的答案跳到我面前之前,让我限定问题并说,并非所有课程都可以子分类。

  • 子类应该是一个经过深思熟虑的额外而不是所有类的默认属性。
  • 可以说类在默认情况下应该是最终的,而不是最终的,如果人们真的想要的话。

我可以想到封装私有类的许多优点。

  • 如果该类不是真正供我查看的,而只是一个我应该考虑实现细节的助手,则将其设为非公开意味着当我在我的 ide 中按名称或接口的实现搜索类时它不会出现。
  • 这也意味着,如果除了导出的接口之外的所有内容都是公开的,那么在浏览库 jar 文件时检查 api 会变得更加简单,因为噪音更少。我不再看到内部实现的东西。
  • 无需担心包外的子类。
  • 对于用户而言,实际好处意味着他们需要的方法也可能会被组合在一个接口/类下,而不是分散在其他公共类中,这意味着更少的混乱,更少的地方可以查看和更少的句柄太管理了。

那么为什么人们不利用这些设施呢?

【问题讨论】:

    标签: java design-patterns class-design


    【解决方案1】:

    有语言,例如python 其中一切都是公开的,没有什么是最终的。这并不意味着他们生活在地狱中,模块无法使用和维护。恰恰相反。

    软件工程很棘手。很多时候,我们都是我们心中的稻草人。这很有趣,我们追求理想而不考虑实际利益——如果我们有时间的话。如果你是一个人负责上百门课,完美主义者会让你发疯。

    【讨论】:

    • 这就是为什么没有人用 Python 这样的语言编写真正的系统。脚本语言适用于快速而讨厌的工作,而不是大型复杂系统。大型系统通常是用类型安全的语言编写的,因为人们讨厌此类事情引起的头痛和困惑。
    • 对此不太确定。只要有很多支持的测试用例,动态语言就不是一件麻烦事(有一个好的IDE)
    • 史蒂文。事实上,脚本语言之所以是动态的,是因为如果一个人正在编写一个最多只有几十行的程序,那么就不需要类型,因为你可以在你面前看到和理解,因为如果有接口和您必须实施并满足他们,这将使程序更长。然而随着事情的发展,类型安全是你的朋友,因为这意味着编译器可以添加一个额外的层并验证你的代码看起来是否正确。您还可以通过重构或改进在中不可能的命名内容来清理代码
    • 脚本语言,因为重构只是一种搜索和替换,无法判断您是否只有某些目标的调用者或所有具有相同名称的函数。
    • @mP:但是人们确实用脚本语言编写真正的系统,尽管你所有的观点都是如此。 “真实系统”是指超过一百万条生产线,用于生产控制安全关键设备,例如工厂生产线、石油钻井平台或海军舰艇上。我知道部署中有这样的事情。因此,通过(多个)反例的演示,您的论点充满了它。
    【解决方案2】:

    基本上,情况会发生变化。 你认为现在不能子类化的东西,以后可能需要子类化。 稍后当您发现实际上应该进行子类化时,会考虑更多“蓬松”的设计选择(例如使类最终化)。

    大多数 API 设计都考虑了可扩展性。

    【讨论】:

    • "你认为现在不能子类化的东西,以后可能需要子类化"。将类从final 转换为非final 很容易,因为这不会破坏任何使用该库的代码。但是做相反的事情会破坏任何定义派生类的代码。
    • 问题是你创建了一个不是为子类化而设计的类而忘记将它定为final。我所说的“未设计”是指您只是没有想到,这可能会导致难以发现错误。对于非最终类,方法a 调用方法b 的事实不再是实现细节,应该记录在案并且永远不会更改。当a 是构造函数时会发生有趣的事情,因为它在尚未完全构造的对象上调用b(例如从ctor 调用的Random.setSeed)。
    • @Raedwald,这正是您今天应该将您的课程设置为最终课程并将包私有的原因。如果它们有用并且应该是可子分类的,那么您总是可以在以后的阶段这样做。一旦你发布了一些公开的东西而不是最终的东西,某个地方的某个人会找到并做子类的事情,这会阻止你添加最终的。等
    • @Steven 大多数 api 设计者没有考虑可扩展性。 java.util.Stack、java.util.Properties 是完美的废话示例,如果 Hashtable 是最终的或包私有的,则可以避免这些废话。子类化在大多数时候是邪恶的,提取一个只选择正确方法的接口几乎总是一个更好的解决方案。人们喜欢子分类的原因是懒惰,他们认为自己在节省时间,直到后来他们回来才意识到他们被卡住了,因为有人对它进行了子分类,所以他们无法更改课程。
    • @mP:这是不应该继承子类的人的错。我主张更灵活且考虑未来的设计。添加不是 100% 必需的限制通常只是与稻草人作斗争,以后会被淘汰。至于人们说你可以稍后删除它,稍后有时为时已晚,因为尝试使用你的 api/library 的可怜的开发人员已经碰到了最后的砖墙,要么已经离开你的 api,要么已经破坏了更多完成他们需要做的事情的丑陋方式。
    【解决方案3】:

    就默认情况下类是否应该是最终的而言,这是一个备受争议的观点,没有真正的“正确”答案。 Java 选择了一种方式,而 C# 选择了另一种方式,不同的人认为不同的语言是对是错。

    就个人而言,我喜欢默认情况下类可扩展的方式,如果人们想让它们成为最终的,那么他们可以,这只是一个关键字。但是在我看来,允许子类化只会增加更多的灵活性,当然我不必在阳光下扩展每个非最终对象,但是如果我想添加某种程度的特定行为,那么我可以使用该选项。是的,可以使用组合(在某些情况下确实应该使用),但这并不也不应该排除子分类作为一种选择。在很多情况下,子类对象满足与父对象的 is-a 关系,在这种情况下,这是正确的方法。诚然,尽管这在过去被滥用了,即使在 Java API 中也是如此(例如,属性并不是真正的哈希表。)但是如果你试图改变语言来阻止人们变得愚蠢——好吧,他们总能找到一个绕过它!

    至于使类包私有,对我来说,如果包是分层的而不是平面的,这个选项会更有用。是的,它们在描述中是分层的,但是在 org.me 中声明的包私有的东西不能从 org.me.subpackage 访问,我经常觉得有必要这样做。幸运的是,通过添加超级包,我们可能会看到这个选项得到了更好的使用!

    【讨论】:

    • 关于子包可见性的最后一点当然是一个问题。我以为我听说会有一个关于引入关键字或解决这个问题的 jsr,但谁知道那里发生了什么。
    • 使用包私有类,他们可以将它们的工厂放在一个具有静态方法的类中——想想 java.util.Collections。无需知道各种同步/不可修改、单例等方法使用的实现的名称,知道返回类型就足够了,它们被隐藏起来 - 幕后类的名称并不重要或没有必要。
    • "Java 选择了一种方式,而 C# 选择了另一种方式" 默认情况下,C# 中没有类不是密封/最终的。默认情况下,只有方法是非虚拟的。
    【解决方案4】:
    1. 我的经验不支持您认为的库不支持子类化的优势。

    2. Java 没有委托,瘦子类通常用作适配器或方法拦截器。 在代码中执行此操作比使用反射代理执行此操作更具可读性。

    情况发生了变化。可以更改的软件更易于使用。有时这需要像 Java 8 的默认方法那样进行彻底的改进。这些在接口被广泛使用后巧妙地修复了向接口添加方法。

    【讨论】:

    • 那是 b/s java 确实支持委托,它并不难生成带有 ide 或喘气的转发方法在你自己输入它们。
    • @mP 是的,您所描述的正是我所描述的。通过瘦子类包装器进行委派。如果所有库类都是最终的,你就不能这样做。 C# 具有委托机制,默​​认情况下类是最终的。我个人认为 Java 和 Smalltalk 做得对。
    • 如果你编程到接口而不是子类,你总是可以委托,那么 final 不会把你搞砸。
    【解决方案5】:

    嗯,这只是因为思考什么不应该被扩展需要大量的脑力劳动。

    对于大多数中小型组件和框架来说,最好的猜测是弃用和保留滥用/过度使用的方法/类比考虑最可能和一些不太可能的用例并偶尔关闭更容易和更用户友好然后需要一些肮脏的委托/装饰而不是简单的扩展。

    在我的实践中,只有 Java 的 Spring 对这种预防性关闭真正困扰着我(我认为在几个微版本之后会产生更清晰的 API)。

    我在这件事上引用了 R. Martin 的话:

    http://java.akraievoy.org/2009/03/openclosed-principle-strategic-closure.html

    【讨论】:

      【解决方案6】:
      • 修饰符 public 和 final 来自不同的类别,当你想清楚时,将它们放在一个句子中并不是一个好的开始
      • 至于 final - 要求发生变化,一段时间后您会发现自己也需要子类化一些 final 类,另外 并非所有库/框架都可以最终类的 crate 代理
      • 至于 包访问 - 可能会使重构更加复杂,但是是的,如果您生成一些库,不发布内部类是一个很好的“模式”李>

      【讨论】:

      • 如果一切都是接口,则无需担心代理最终类。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-17
      • 2020-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多