来自 C++ 背景的我必须掌握 Java 世界及其框架的复杂性。查看 DI 的 spring 框架,我发现我很难相信我必须将每个 setter 函数都公开给 DI。这个要求不是破坏了信息隐藏的原则吗?
当然,我希望 spring 能够设置我的类的一些私有部分,但我当然不希望每个客户端类都能够这样做。
我在这里想念什么?
来自 C++ 背景的我必须掌握 Java 世界及其框架的复杂性。查看 DI 的 spring 框架,我发现我很难相信我必须将每个 setter 函数都公开给 DI。这个要求不是破坏了信息隐藏的原则吗?
当然,我希望 spring 能够设置我的类的一些私有部分,但我当然不希望每个客户端类都能够这样做。
我在这里想念什么?
我同意你的观点——这就是我更喜欢构造函数注入的原因。
如果您对接口进行编码,则只需要在实现上公开设置器。当您将接口注入系统的其他部分时,它们无法访问对象的实现细节或状态。
您(可能)必须制作一个 setter,它将通知外部您的一些内部细节,但没有必要制作一个 getter。所以你透露了一些信息,但不是太多;除了其预期目的之外,它对任何事情都没有真正的用处。
此外,我只建议使用注释和@Autowired,在这种情况下,您不需要创建公共设置器。
如果您使用弹簧注释(@Autowired),您可以 DI 私有成员。
在我看来,如果您追求松散耦合和(单元)可测试性,那么 DI 会弹出不应隐藏的信息。
从未使用过@Autowired,我倾向于在构造函数中使用参数,但有时很难理解参数的含义,特别是如果您有很多参数 - 在这种情况下,我更喜欢使用 Effective 中描述的“Builder”方法爪哇。构造函数接收构建对象(具有设置器),并用它构造自己。类的注入属性是最终的(不变性),“Builder”类包含setter但不包含getter(它不需要,因为我们将其声明为正在构造的类的内部类),并且不需要setter仅为 Spring 创建:
<bean id="runnable" class="MyClass">
<constructor-arg>
<bean class="MyClass$Builder">
<property name="p1" value="p1Value"/>
<property name="p2" value="p2Value"/>
<property name="p3" value="p3Value"/>
<property name="p4" value="p4Value"/>
</bean>
</constructor-arg>
</bean>
班级代码:
public class MyClass {
private final String p1;
private final String p2;
private final String p3;
private final String p4;
public MyClass(Builder builder) {
this.p1 = builder.p1;
this.p2 = builder.p2;
this.p3 = builder.p3;
this.p4 = builder.p4;
}
...
public static class Builder {
private String p1;
private String p2;
private String p3;
private String p4;
public void setP1(String p1) {
this.p1 = p1;
}
... and so on
}
}
我想这是一个权衡。你减少了硬连线的依赖,但你可能会暴露你的实现的胆量。使用正确的抽象,您也可以减少这种情况,但随后会增加代码库的复杂性(例如,具有可以是 LDAP 连接或 SQL 连接的通用“连接”)。
就个人而言,我认为使用构造函数注入也无济于事,因为它更具概念性。
我将不得不检查@Autowire,tho'。
tj
只是想提一下,我(无意中)发布了这个问题的更通用版本,并且对这个问题有一些进一步的见解:依赖注入必须以封装为代价吗?
不同意 Spring 打破封装的观点。即使你有 pojo,你已经在类中公开了并且第三方使用了你的 jar,jar 的消费者仍然有可能做同样的事情,这在 Spring bean 配置中是可能的。(适用于任何其他 OOP 语言)
Spring 只是提供了可以通过代码完成的方法,并且该控件移出代码(IOC)。
消费者(考虑其他程序员使用您的库),通过 spring 配置创建 bean,您的代码仍然可以控制。没有人阻止您在 setter 中验证用户给出的输入(spring IOC 框架也通过其他类调用您的 setter 完成的相同代码)。
public void setAge(int age) {
if ( age < 0 ) {
throw new AppException("invalid age!");
}
this.age=age;
}
这也是我更喜欢 Guice 的另一个原因;) Guice 和 Spring 都实现了 JSR 330 DI 规范,但是使用 Guice,我可以在没有设置器的情况下注入我的私有实例字段,我真的不喜欢构造函数注入,因为它似乎更难重构。在我的拙见中,它也只是更多的打字而没有多大价值。
后来,迪恩