During the last decade, web enterprise application development has seen a continuous displacement from excellent OOP (Object Oriented Practices).
To understand this, we chose to examine the usage of constructors.
The Situation:
Facilities provided by IOC frameworks like Spring have been slowly shifting away from constructors. Many programmers now tend to rely on the framework to set up an object’s dependencies in a careless manner. It is very common to find code like this:
public class MyClass{
@Autowired
private AnotherService1 dependency1;
@Autowired
private AnotherService2 dependency2;
@Value("#{props.prop1}")
private String accessCode;
public MyClass(){
super();
}
public void doSomething(String s){
Object foo = dependency1.doSomething(s);
…..
dependency2.doAnotherThing(foo, accessCode);
}
public void setDependency1(AnotherService1 s1){..};
public void setDependency2(AnotherService1 s2){..};
public void setAccessCode(String code){...};
}
Also dependencies declared in XML use setters in a similar fashion.
This code suffers from bad form and has several weaknesses.
- You can’t easily find its dependencies: you need to go through all the annotated fields or setters to determine them.
By adding setters, you are making your object mutable. So, if our “MyClass” object was a service (singleton) used in several parts of your enterprise application, anyone could inadvertently or intentionally change its status, which could result in unpredictable consequences. This is not the only reason to shy away from mutable objects.
- Suppose you need to use this class somewhere else, outside an IOC context. You need to instantiate this object programmatically; again, you can’t easily determine when this object has all its dependencies setup and is ready. Moreover, you can’t tell if there is a required order in the way the fields or dependencies are set up.
This becomes even worse if you just have the jar and no access to the source file.
Eg:
MyClass c = new MyClass();
c.setDependency1(d1);
c.setDependency2(d2);
c.setAccessCode(“xyz”);
- Let’s suppose you don’t want to write setters, since, for instance, a framework like Spring can still inject values into private fields! Later you need to do a Unit test to this class, at which point you have two options: add the setters, or rely on some reflection “black magic” to set up its field values, which, in turn, generates even worse effects. For instance, some people make use of things like Spring’s ReflectionTestUtils:
ReflectionTestUtils().setField( theTestSubject, “accessCode”, “XWYZ”);
This not only is harder to read in a test, but it also makes the code harder to refactor and loses compile time checking, making it prone to fail only at run time.
In addition, it is still possible to create an instance calling the parameterless constructor (added by the developer or the compiler if one is not provided). So, what if there is an issue supplying the dependencies (using the reflection magic or the corresponding setter methods)?
Answer: the application will end up having an object with the wrong state that will inevitably throw NullPointerException when calling methods on it that rely on an unsatisfied dependency.
A class has to make sure by itself that the public mechanisms to create instances of itself are good enough to provide objects with a safe state.
The solution – Using constructors:
The truth is that all the previously mentioned issues is not Spring’s fault. You can have them all or almost all of them in any application regardless of the framework or using no framework at all.
Let’s rewrite the previous example, but now using a constructor.
public class MyClass{
private final AnotherService1 dependency1;
@Autowired
private final AnotherService2 dependency2;
private final String accessCode;
@Autowired
public MyClass(AnotherService1 d1, AnotherService2 d2, @Value("#{props.prop1}") ac) {
this.dependency1 = d1;
this.dependency2 = d2;
this.accessCode = ac;
}
public void doSomething(String s) {
Object foo = dependency1.doSomething(s);
…..
dependency2.doAnotherThing(foo, accessCode);
}
}
Now:
- Dependencies can still be injected by Spring (using annotations or XML since both support constructors).
- The code is concise, clear, and tidy.
- It is clear that the object is complete and ready-to-use as soon as it is created.
- Private fields are immutable.
- When creating an instance programmatically, it is easy to see the required dependencies:
MyClass c = new MyClass(d, d2, “xyz”);
- It is not necessary to rely on setters or reflection facilities providers anymore for setting dependencies.
Embracing constructors across all of the project makes the whole code easier to implement and maintainable across time, for the current and next generation of developers that will have to deal with it. This also reduces time, code complexity, and saves money.
Another benefit is that measuring the amount of dependencies involved in a constructor can be a sign that a redesign may be needed. Too many dependencies may mean that the class could be doing many more things than what it should be responsible for, and should be partitioned into several classes with proper responsibilities. The same scenario applies for circular dependencies: if you find one in your code, you may want to review the design.
Summing up:
To sum up, and in case all of the above was not enough to convince the reader, we would like to quote part of Spring Framework 2.5.x till 3.X documentation:
“The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional. Setter methods also make objects of that class amenable to reconfiguration or re-injection later.” see
Also, see this quote from Spring 4.1.x:
“The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.” see
Interesting, isn’t it? :)
In the future we will address the benefits of using static builder methods as well.
Hope you have enjoyed this blog post, and opinions are welcomed.
The post Embracing Constructors appeared first on AAJ Technologies.