【问题标题】:The missing "framework level" access modifier缺少“框架级别”访问修饰符
【发布时间】:2012-05-26 02:29:55
【问题描述】:

这是场景。作为公开许可的开源 API 的创建者,我的团队创建了一个基于 Java 的 Web 用户界面框架(那么还有什么新东西?)。为了在 Java 中保持良好和有序,我们使用了具有命名约定的包 org.mygroup.myframework.x,其中 x 表示组件、验证器、转换器、实用程序等(同样,还有什么新内容?)。

现在,在类 org.mygroup.myframework.foo.Bar 中的某处是一个方法 void doStuff(),我需要执行特定于我的框架的逻辑,并且我需要能够从我的其他几个地方调用它框架,例如 org.mygroup.myframework.far.Boo。鉴于 Boo 既不是 Bar 的子类,也不是在完全相同的包中,所以必须将方法 doStuff() 声明为 public 才能被 Boo 调用。

但是,我的框架是作为一种工具存在的,它允许其他开发人员为他们的客户创建更简单、更优雅的 R.I.A.。但是如果 com.yourcompany.yourapplication.YourComponent 调用 doStuff(),它可能会产生意想不到的不良后果。我会 宁愿这永远不会被允许发生。 请注意,Bar 包含其他真正公开的方法。

在象牙塔世界中,我们将重写 Java 语言并插入一个标记化的模拟来默认访问,这将允许我们选择的包结构中的任何类访问我的方法,可能看起来类似于:

[org.mygroup.myframework.*] void doStuff() { .... }

通配符表示包以 org.mygroup.myframework 开头的任何类都可以调用,但不能调用其他类。

鉴于这个世界并不存在,我们还有什么好的选择?


请注意,这是由现实生活场景驱动的;为了保护罪犯,他们改名了。存在一个真正的框架,在其整个 Javadoc 中,人们会发现公共方法被注释为“此方法是 MYFRAMEWORK 的内部而不是 其公共 API 的一部分。不要调用!!!!!!”一项小研究表明这些方法是从框架内的其他地方调用的。

事实上,我是一名使用相关框架的开发人员。尽管我们的应用程序已部署并取得了成功,但我的团队经历了如此多的挑战,以至于我们想说服我们的老板不要再使用这个框架了。我们希望通过对框架开发人员做出的糟糕设计决策进行深思熟虑的介绍来做到这一点,而不仅仅是咆哮。这个问题将是我们的一个(几个)观点,但我们无法指出我们可能会如何以不同的方式做到这一点。我的工作场所已经在这里进行了一些热烈的讨论,所以我想知道世界其他人会怎么想。


更新:到目前为止,这两位回答者没有冒犯,但我认为你错过了标记,或者我没有很好地表达它。无论哪种方式,都可以让我尝试阐明事物。尽可能简单地说,框架的开发人员应该如何重构以下内容。请注意,这是一个非常粗略的示例。

package org.mygroup.myframework.foo;
public class Bar {
     /** Adds a Bar component to application UI */
     public boolean addComponentHTML() {
         // Code that adds the HTML for a Bar component to a UI screen
         // returns true if successful
         // I need users of my framework to be able to call this method, so
         // they can actually add a Bar component to their application's UI
     }

     /** Not really public, do not call */
     public void doStuff() {
         // Code that performs internal logic to my framework
         // If other users call it, Really Bad Things could happen!
         // But I need it to be public so org.mygroup.myframework.far.Boo can call
     }
}

另一个更新:所以我刚刚了解到 C# 具有“内部”访问修饰符。因此,表达这个问题的更好方法可能是“如何在 Java 中模拟/模拟内部访问?”尽管如此,我并不是在寻找新的答案。我们的老板最终同意了上面提到的担忧

【问题讨论】:

  • 听起来像是将公共 API 和实现拆分为单独项目的好案例,公共 API 由大量接口组成。
  • @Perception:我相信这就是 JSF 所做的,使用单独的 jsf-api.jar 和 jsf-impl.jar。
  • There exists a real framework where peppered throughout its Javadoc one will find public methods commented as... 嗯,我想知道可能是哪个框架:))
  • 为每个要保密的方法添加一个参数“密码”,如果密码不正确,则抛出 IllegalArgumentException。 ;)
  • Eclipse Java 中的*.internals 约定怎么样?见stackoverflow.com/questions/551872/…

标签: java access-modifiers


【解决方案1】:

当您提到文档问题时,您最接近答案。真正的问题不是你不能“保护”你的内部方法;相反,内部方法污染了您的文档并引入了客户端模块可能错误地调用内部方法的风险。

当然,即使您确实拥有细粒度的权限,您仍然无法阻止客户端模块调用内部方法——无论如何,jvm 并不能防止基于反射的私有方法调用.

我使用的方法是为每个有问题的类定义一个接口,并让该类实现它。接口可以仅根据客户端模块进行记录,而实现类可以提供您想要的内部文档。如果您不想在分发包中包含实现 javadoc,您甚至不必包含,但无论哪种方式,边界都已明确划分。

只要您确保在运行时每个文档界面只加载一个实现,现代 jvm 将保证您不会因使用它而遭受任何性能损失;并且,您可以在测试期间加载线束/存根版本以获得额外奖励。

【讨论】:

    【解决方案2】:

    为了提供这个缺失的“框架级访问修饰符”,我能想到的唯一想法是 CDI 和更好的设计。 如果您必须在各种(但很少)情况下使用来自非常不同的类和包的方法,那么肯定会有一种方法来重新设计这些类,以使这些方法“私有”且不可访问。

    【讨论】:

    【解决方案3】:

    Java 语言不支持这种访问级别(您想要类似“内部”的名称空间)。您只能限制对包级别(或已知的继承公有保护私有模型)的访问。

    根据我的经验,您可以使用 Eclipse 约定: 创建一个名为“internal”的包,该包的所有类层次结构(包括子包)都将被视为非 API 代码,并且可以随时更改,而不保证您的用户。在该非 API 代码中,您可以随时使用公共方法。由于这只是一个约定,并且不是由 JVM 或 Java 编译器强制执行的,因此您不能阻止用户使用该代码,但至少要让他们知道这些类不打算由 3rd 方使用。

    顺便说一句,在 Eclipse 平台源代码中,有一个复杂的插件模型,它通过为每个插件实现自定义类加载器来强制您不要使用其他插件的内部代码,以防止加载这些应该是“内部”的类插件。

    【讨论】:

    • 我的问题是我调用 org.mygroup.myframework.foo.Bar 的类既包含我希望其他应用程序能够调用的公共方法,也包含我不希望调用的公共方法。
    【解决方案4】:

    有时使用接口和动态代理来确保您只公开您确实想要公开的方法。

    但是,如果您的方法被非常频繁地调用,那么这会带来相当高的性能成本。

    使用@Deprecated 注释也可能是一种选择,虽然它不会阻止外部用户调用您的“框架私有”方法,但他们不能说他们没有被警告过。

    一般来说,我认为您不必担心您的用户故意过多地朝自己的脚开枪,只要您向他们明确表示他们不应该使用某些东西。

    【讨论】:

      猜你喜欢
      • 2015-02-11
      • 1970-01-01
      • 2015-11-26
      • 2014-11-03
      • 2020-02-05
      • 2015-10-28
      • 1970-01-01
      • 1970-01-01
      • 2011-01-15
      相关资源
      最近更新 更多