Constructor or Setter?
When creating an object should we inject everything at construction time or specify optional attributes via setters.
Join the DZone community and get the full member experience.
Join For FreeIt goes without saying that every object needs to be created before it can be used. It does not matter whether we are talking about a domain, frameworks, libraries, or any other type class. When your code is Object-Oriented, those classes are only definitions of the objects. You cannot use objects before they are created.
When we are talking about object’s initialization, we often need to think about dependencies. How will you inject them? Will you use the constructor or setter?
Let me help you make the right decision.
Once Upon a Time...
… there was a need to handle some event. To do so, we had to, first of all, retrieve necessary data from the Repository and then pass it to the Trigger which was responsible for triggering an appropriate action based on given data.
During implementation we created the following class:
public class SomeHandler {
public SomeHandler(Repository repository, Trigger trigger) {
// some code
}
public void handle(SomeEvent event) {
// some code
}
}
Things always change, though. Our customer told us that from time to time they would need to store some information retrieved from the repository before an appropriate action is taken. They need this data for statistical purposes and further analysis.
After the change this is what our class looked like:
public class SomeHandler {
public SomeHandler(Repository repository, Trigger trigger) {
// some code
}
public SomeHandler(Repository repository, Trigger trigger, SnapshotTaker snapshotTaker) {
// some code
}
public void handle(SomeEvent event) {
// some code
}
}
Another month goes by and another requirement comes from our client. They want to have the possibility of enabling notifications just after triggering an event. This is necessary for them in case of some emergency issue. They want to have higher transparency.
Ok, now we’ve got two things that may be enabled:
public class SomeHandler {
public SomeHandler(Repository repository, Trigger trigger) {
// some code
}
public SomeHandler(Repository repository, Trigger trigger, SnapshotTaker snapshotTaker) {
// some code
}
public SomeHandler(Repository repository, Trigger trigger, Notifier notifier) {
// some code
}
public SomeHandler(Repository repository, Trigger trigger, SnapshotTaker snapshotTaker, Notifier notifier) {
// some code
}
public void handle(SomeEvent event) {
// some code
}
}
The code looks good, doesn’t it? Ok, that was a rhetorical question. Let’s do something about it.
Constructor or Not?
In the example above we’ve got the class with four constructors. Why so many? Because of the changing needs of our customer. And this is perfectly fine. An application should satisfy a client’s needs.
Where is the problem? The problem is with the design of the class.
Why do we have so many constructors? Because some dependencies are optional, their presence depends on external conditions.
Do we need so many constructors?
Before we answer this question, it is good to ask different one: what’s the purpose of the constructor?
We should create an object in a valid state. We should not allow to create an instance if there is something more that hasto be done to make an object usable. That’s why all required dependencies should be placed in a constructor.
On the other hand, we should place in the constructor only the required dependencies. Constructor is not a place for anything optional. If something is optional, this means we don’t need it to create a valid object.
If we would like to use other dependencies that are nice to have we should inject them in different way. And this is where setters come into play. We are not forced to invoke setter method. We may have a need, but this is not required. You should use setters when dependency is an option.
So, do we need so many constructors? Let the code be the answer:
public class SomeHandler {
public SomeHandler(Repository repository, Trigger trigger) {
// some code
}
public void setSnapshotTaker(SnapshotTaker snapshotTaker) {
// some code
}
public void setNotifier(Notifier notifier) {
// some code
}
public void handle(SomeEvent event) {
// some code
}
}
Less code and more descriptive. From the first moment you know what is required and what just might be used.
But Wait! Setter?!
I don’t like setters. Why? Because those methods in some way break encapsulation. But what can we use instead of setters? What can be used instead in a given example?
Well, we will not avoid those methods. Or, to be more precise — we need their functionality. There is a need to let the customer enable the functionality. In a given example mutators needs to stay because they’re needed. However, we can always make the code better. More domain-related. How? We just need to show this relation with the domain:
public class SomeHandler {
public SomeHandler(Repository repository, Trigger trigger) {
// some code
}
public void enable(SnapshotTaker snapshotTaker) {
// some code
}
public void enable(Notifier notifier) {
// some code
}
public void handle(SomeEvent event) {
// some code
}
}
I wrote that I don’t like setters, because their break encapsulation, but this is not only about the method’s functionality itself. Another problem with using methods like setX is that even their names are implementation-oriented. Sometimes setter functionality is necessary. However, remember to name a method in a way that will show domain connotation.
Too Many Options
Sometimes too many options also pose a problem. It can be a sign that you’re violating Single Responsibility Principle.
If there are too many options it might mean that there are too many responsibilities and it is worth re-thinking your current solution.
Be very careful whenever adding another optional part into the code of the class. Maybe this class is doing too much?
Word At the End
Hopefully you found the article useful.
You should know now that you ought to place only required dependencies in your constructors. Any optional dependencies require other well-named methods.
What’s next?
Let’s go and create some objects! :)
Published at DZone with permission of Sebastian Malaca, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments