DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Clean Unit Testing
  • Cutting-Edge Object Detection for Autonomous Vehicles: Advanced Transformers and Multi-Sensor Fusion
  • While Performing Dependency Selection, I Avoid the Loss Of Sleep From Node.js Libraries' Dangers
  • Why I Started Using Dependency Injection in Python

Trending

  • Build a Simple REST API Using Python Flask and SQLite (With Tests)
  • MySQL to PostgreSQL Database Migration: A Practical Case Study
  • Securing the Future: Best Practices for Privacy and Data Governance in LLMOps
  • Start Coding With Google Cloud Workstations
  1. DZone
  2. Coding
  3. Languages
  4. Constructor or Setter?

Constructor or Setter?

When creating an object should we inject everything at construction time or specify optional attributes via setters.

By 
Sebastian Malaca user avatar
Sebastian Malaca
·
Feb. 26, 16 · Opinion
Likes (48)
Comment
Save
Tweet
Share
32.3K Views

Join the DZone community and get the full member experience.

Join For Free

It 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! :)

Dependency Object (computer science)

Published at DZone with permission of Sebastian Malaca, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Clean Unit Testing
  • Cutting-Edge Object Detection for Autonomous Vehicles: Advanced Transformers and Multi-Sensor Fusion
  • While Performing Dependency Selection, I Avoid the Loss Of Sleep From Node.js Libraries' Dangers
  • Why I Started Using Dependency Injection in Python

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!