14

Coming from a C++ background I have to master the complexity of the Java world and its frameworks. Looking at the spring framework for DI I am finding it difficult to believe that I have to make each setter function which will be subject for DI public. Doesn't that requirement break the principle of information hiding?

Of course I want spring to be able to set some private parts of my classes, but I certainly do NOT want every client class to be able to do the same.

What am I missing here?

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
struppi
  • 595
  • 1
  • 3
  • 15

10 Answers10

15

I agree with your point - that's why I prefer constructor injection.

duffymo
  • 305,152
  • 44
  • 369
  • 561
6

If you code to interfaces, you only need to expose setters on the implementations. As you inject the interfaces into other parts of the system, they cannot access the implementation details or state of the objects.

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • 1
    This answer implies that "coding to interfaces" means that every class must implement a separate interface. This isn't what the principle, as described in the GoF book, means. In reality, it only applies for cases where you need, or already have, two separate object types, one for the abstraction (interface) and another for one implementation of that abstraction. – Rogério Jan 27 '10 at 14:46
  • I think you are likely to find that the classes which need to be injected into each other in a common Spring application are **service** classes, such as `Dao`s etc. I have found that a large number of these could conceivably have alternate implementations – oxbow_lakes Jan 27 '10 at 14:59
  • 1
    Yes, when you have multiple implementations the "code to the interface, not the implementation" principle does apply. But saying things like "if you code to interfaces ..." without a clear context is misleading, and can easily be mis-interpreted by less experienced developers. Overuse of separate interfaces causes great harm, IMO. – Rogério Jan 28 '10 at 12:12
  • 1
    What harm? Admittedly in bloats the codebase but starting with an interface rather than an implementation is positive from the aspect of forcing you to think about what abstractions exist within the problem you are solving – oxbow_lakes Jan 28 '10 at 13:04
5

You (may) have to make a setter, which will inform the outside of some of your internal details, but there's no need to make a getter. So you are revealing some information, but not really too much; it's not really useful for anything but its intended purpose.

Additionally I'd just recommend using annotations and @Autowired, in which case you do not need to make a public setter.

krosenvold
  • 75,535
  • 32
  • 152
  • 208
  • +1 - advocating annotations wherever possible is a good thing. I'm trying to make the jump myself now, but it's hard to break that XML habit. – duffymo Mar 09 '09 at 20:21
3

If you use spring annotations (@Autowired) you can DI private members.

If you go for loose coupling and (unit)testability in my view springs DI breaks out information that should not be hidden.

Kees de Kooter
  • 7,078
  • 5
  • 38
  • 45
1

Never used @Autowired, I tend to like using parameters in the constructors but sometimes it's hard to understand what the parameters mean, specially if you have a lot of parameters - in that case, I prefer to use the "Builder" approach described in Effective Java. The constructor receives the build object (which has setters), and constructs itself with it. The injected attributes of the class are final (immutability), the "Builder" class contains setters but not getters (it doesn't need to as we declare it as an inner class of the class that is being constructed), and no setters need to be created just for 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>

Class code:

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
   }
}
Ravi Wallau
  • 10,416
  • 2
  • 25
  • 34
1

I had basically the same question here:

Encapsulation in the age of frameworks

I think the answer might be constructor injection. Exposing your properties with setters makes it really hard to ecapsulate anything and maintain good object state.

Community
  • 1
  • 1
Andy White
  • 86,444
  • 48
  • 176
  • 211
0

Not agreed with the point that Spring breaks encapsulation. Even if you have pojo where you have get and set exposed in class and third party consumes your jar, still chances that consumer of jar do the same which is possible with the Spring bean configuration. (true for any other OOPs language)

Spring just provides way which can be done through the code and that control moved out of the code (IOC).

Consumer (consider other programmer uses your library), creates bean through the spring configuration, still your code have control. No body stops you to validate input given by the user(spring IOC framework also goes through the same code which is done by other class calling your setter) in setter.

public void setAge(int age) {
 if ( age < 0 ) {
   throw new AppException("invalid age!");
 }
 this.age=age;
}
neo
  • 1,054
  • 1
  • 10
  • 19
0

Just wanted to mention that I've (inadvertently) posted a more generic version of this question and that it has some further insights into the issue: Must Dependency Injection come at the expense of Encapsulation?

Community
  • 1
  • 1
urig
  • 16,016
  • 26
  • 115
  • 184
0

And that is another reason I prefer Guice ;) Both Guice and Spring implement JSR 330 DI spec but with Guice I can inject into my private instance fields with no setters an I really dislike constructor injection as it has seemed harder to refactor. It is also just alot more typing for not much value in my humble opinion.

later, Dean

Dean Hiller
  • 19,235
  • 25
  • 129
  • 212
0

I suppose it is a tradeoff. You reduce hard-wired dependencies, but you potentially expose the guts of your implementation. With the right abstractions, you can reduce that as well, but then you increase the complexity of the code base (for instance, having a generic "Connection" that could be an LDAP connection or an SQL connection).

Personally, I don't think using constructor injection helps with it, either, because it is more conceptual.

I will have to check out @Autowire, tho'.

tj

Travis Jensen
  • 5,362
  • 3
  • 36
  • 40