8

这是场景。作为公开许可的开源 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 调用。

然而,我的框架作为一种工具存在,允许其他开发人员为他们的客户创建更简单、更优雅的 RIA。但是如果 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 中模拟/模拟内部访问?” 尽管如此,我并不是在寻找新的答案。我们的老板最终同意了上面提到的担忧

4

4 回答 4

0

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

于 2012-05-18T14:01:35.757 回答
0

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

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

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

于 2012-05-18T14:07:45.970 回答
0

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

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

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

总的来说,我认为你不应该担心你的用户故意在脚上射击自己太多,只要你向他们明确表示他们不应该使用某些东西。

于 2012-05-31T16:11:27.233 回答
0

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

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

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

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

于 2012-06-12T01:03:22.103 回答