My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
KeepConstructorsHidden  
Updated Oct 16, 2011 by sberlin

Keep constructors on Guice-instantiated classes as hidden as possible.

Consider this simple interface:

public interface DataReader {
 
  Data readData(DataSource dataSource);
}

It's a common reflex to implement this interface with a public class:

public class DatabaseDataReader implements DataReader {
  
   private final ConnectionManager connectionManager;

   @Inject
   public DatabaseDataReader(
      ConnectionManager connectionManager) {
     this.connectionManager = connectionManager;
   }

   @Override
   public Data readData(DataSource dataSource) {
      // ... read data from the database
      return Data.of(readInData, someMetaData);
   }
}

A quick inspection of this code reveals nothing faulty about this implementation. Unfortunately, such an inspection excludes the dimension of time and the inevitability of an unguarded code base to become more tightly coupled within itself over time.

Similar to the old axiom, Nothing good happens after midnight, we also know that Nothing good happens after making a constructor public: A public constructor will have illicit uses introduced within a code base. These uses necessarily will:

  • make refactoring more difficult.
  • break the interface-implementation abstraction barrier.
  • introduce tighter coupling within a codebase.

Perhaps worst of all, any direct use of a constructor circumvents Guice's object instantiation.

As a correction, simply limit the visibility of both your implementation classes, and their constructors. Typically package private is preferred for both, as this facilitates:

  • binding the class within a Module in the same package
  • unit testing the class through means of direct instantiation

As a simple, mnemonic remember that public and @Inject are like Elves and Dwarfs: they can work together, but in an ideal world, they would coexist independently.

Comment by kwhittin...@gmail.com, Aug 8, 2009

You tell us what not to do but not what we should do? Private class? Private ctors?

Comment by project member dha...@gmail.com, Aug 8, 2009

Package local/private class is my preference. Or the same for (or protected) constructor depending on your use case. A lot of this depends on how the specific class in intended to be used.

The doc's main point is, prefer depending on interfaces and let Guice call new, as this gives you the maximum amount of control and flexibility of design.

Comment by aviad.be...@gmail.com, Mar 11, 2011

Wouldn't making the ctor or class private or package protected make it impossible to test?

Comment by cgdec...@gmail.com, Mar 11, 2011

No, tests should be in the same package as the class.

Comment by diego...@gmail.com, Jul 26, 2011

Really bad explanation.

If you use a private constructor now your classes depends on the DI framework, they are more difficult to test: how do you write an unit test for DatabaseDataReader? without using Guice or other framework that supports the @Inject annotation? The answer is to put the unit tests in the same package, but sometimes you want to make unit test for other classes in the same project module (but in other package) that use the implementation, yes you can use mock objects but an abuse of them also makes unit test difficult to maintain.

  • make refactoring more difficult.
Why? Show me a case where refactoring is more difficult because of the constructor.
  • break the interface-implementation abstraction barrier.
What do you mean with that? You are writing an implementation, making the constructor private doesn't make the dependency to disappear. Is bad if the dependency is on the interface, but for the implementation hiding the dependency behind a framework feature only makes things worst.
  • introduce tighter coupling within a codebase.
  • Non sense, if you use a private constructor your class is coupled with an @Inject compliant framework

As a simple, mnemonic remember that public and @Inject are like Elves and Dwarfs: they can work together, but in an ideal world, they would coexist independently.

LOL good luck when you want to use your implementation in other project that doesn't use Guice

Comment by kopachev...@gmail.com, May 13, 2012

Agreed with diego... alsmost on 99%, this topic isn't convince me as well.

I don't like idea to be restricted to keep module class (or provider) in same package as interface implementation, it not always make sence, I could have tons of interfaces and implementation in one project and I'll still have to pay same efforts during refactor, changing module or provider instead of class constructor.


Sign in to add a comment
Powered by Google Project Hosting