3

我正在开发一个具有这种结构的机器人程序(我将包括层的作用只是为了笑)

  • A 层 - 用于处理按钮、操纵杆等的 GUI简单界面。将这些值转换为发送到控制界面的命令。

  • B 层 - 控制评估来自数据库的设备读/写条目和来自 gui 的命令请求,以便为数据库计算新的设备写条目

  • C 层 - 设备的数据库逻辑表示,创建从设备写入和读取的值的历史记录

  • D 层 - 硬件与物理硬件对话。将设备写入条目转换为命令并将它们发送到设备。使用来自设备的值更新数据库设备读取条目。

我想创建一个 java 应用程序,其中任何层都不能调用高于或低于一层的函数。

是否可以使用包隐私或像工厂这样的模式来创建项目结构,这使得代码无法在层 A 中从 D 层或 C 层导入任何内容?

4

2 回答 2

3

TL;DR 没有一种灵丹妙药的解决方案,但可以利用许多不同的工具

有多种不同的技术可以隔离软件应用程序的不同部分,但我认为没有任何一种解决方案可以解决所有问题。一些构建系统可以限制目标之间的依赖关系(例如,Bazel在构建目标上有一个visibility属性,可以防止一个目标依赖于另一个目标,即使它们通过 Java 的类可见性彼此可见),可以与 Java 的内置函数结合使用能见度。例如:

 // Foo.java
 package com.yourcompany.foo;
 public class Foo {}

 // Build rule for Foo.java
 java_library(
    name = "Foo",
    srcs = ["Foo.java"],
    # Restricts visibility to this directory, even though
    # the class visibility was "public" 
    visibility = ["//visibility:private"],
 )

 // Bar.java
 package com.yourcompany.bar;

 import com.yourcompany.foo.Bar; // prevented by build visibility system

 public class Bar {
    Foo foo = new Foo();
 }

也可以使用接口来调解逻辑组件之间的所有交互并隐藏这些接口的实现(例如,仅通过服务注册接口或通过接口依赖注入来公开实现)。例如,使用Dagger,您可以为每一层创建一个单独的组件,这将允许您编写如下代码:

final class ControllerImpl implements Controller {
   // Since "ControllerImpl" is instantiated / wired into the
   // controller layer, the database dependency is available /
   // exposed for injection within this layer. The access control is
   // strictly performed by the way the dependencies are wired.
   @Inject
   public ControllerImpl(Database database) {
     // ...
   }
}

除了上述之外,您还可以使用依赖分析/依赖分析测试或提交挂钩来自动检测依赖规则违规(并根据它们触发错误/拒绝提交)。例如,穷人的解决方案是简单地扫描每个文件的包声明和导入语句,然后使用一些启发式方法来检测不良依赖关系。

另一种方法是将不同的组件捆绑在单独的 JAR 中并使用自定义ClassLoader加载它们,这将允许您使用反射防止非法访问(否则可能会绕过任何程序结构)。

除了自动化方法之外,手动方法也有其价值。手动方法包括定期代码审查和在这些代码审查和审计期间强制执行的策略。

简而言之,没有一个正确的答案。有必要结合使用几种不同的方法,具体取决于这种分离的重要性。

于 2015-07-19T01:47:47.647 回答
2

这不是单独使用访问修饰符可以实现的。

您也不能通过(以某种方式)控制来做到这一点import......因为Java语言没有对导入设置任何(额外)限制。(该import指令实际上只是语法糖,因此您无需在任何地方使用完全限定的名称。)

那你还能做什么?

  • 您可以尝试实施运行时限制以防止错误的层访问工厂对象。但这种限制很容易被有意或无意地颠覆。

  • 您可以使用某种内部“能力”或“凭证”机制,但很难看出您将如何防止凭证泄漏。(如果凭据由可能有效的安全管理器(见下文)管理,但这会使问题变得更加复杂。)

我认为你可以做到的唯一方法是实现一个 custom SecurityManager,并在每次有潜在的跨层调用时实施安全检查。例如,安全管理器可以(尽管代价高昂)检查调用堆栈以查找调用它的方法/类/包。您还需要关闭某些可用于(通常)颠覆安全管理器的反射操作。本质上,除了内环之外的所有代码都需要被视为“不受信任”的代码。

坦率地说,使用具有“防黑客”安全性的 JVM 实现这种事情可能超出了凡人的能力。(Sun / Oracle 还没有成功....)


其他选择是:

  • 依靠程序员的纪律。

  • 依赖代码库的静态分析;例如,在记录访问规则的注释的帮助下。您需要编写自己的代码分析器来执行此操作。

  • 在各层之间使用地址空间分离、占用空间小、注重安全的 API。(我们这里不再讨论单一的传统 JVM ......)

于 2015-07-19T01:45:28.480 回答