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,可以创建用于每个层的单独component,这将允许你写类似的代码:
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) {
// ...
}
}
除了上述,可以使用依赖关系分析/依赖性分析测试或commit钩子自动检测依赖性规则违规(并根据它们触发错误/拒绝提交)。例如,一个穷人的解决方案就是简单地扫描每个文件的包声明及其导入语句,然后采用一些启发式方法来检测不良依赖。
另一种方法是将不同的组件捆绑到单独的JAR中,并使用自定义的ClassLoader加载它们,这样可以防止使用反射进行非法访问(否则可能会绕过任何程序结构)。
除了自动化方法,手动方法也有其价值。手动方法包括在代码审查和审计期间执行的常规代码审查和策略。
总之,没有一个正确的答案。有必要结合使用几种不同的方法,具体取决于这种分离的重要性。
刚刚向Michael Aaron Safyan提出了同样的问题,但在您看来,这些昂贵/涉及的方法对于清晰的代码维护来说是否现实?还是程序员自律? – flakes
如果您只关心代码可维护性,程序员纪律就足够了。 IMO。 –