There was a time when the Singleton pattern was the darling pattern of many developers. It's a unique feature to keep certain services available globally, makes it very useful in all kinds of applications. It was also one of the most used design patterns among Java developers. I think the main reason for the Singleton pattern's popularity of designing the application is that there is always one instance of the most important service like Manager and Data Access classes. Still, with the adoption of Dependency injection, Singleton slowly faded away, and now it's considered an anti-pattern in Java applications.
In this article, you'll learn why Singleton is anti-pattern now and what happens when you use the Singleton pattern in Java. Along the way, you will also learn about dependency injection, a better alternative to the Singleton pattern, and several benefits it offers.
Comes with Dependency injection, TDD, and Unit testing, and slowly programmer started realizing how to test Singleton was tough to mock. All the code which was calling Singleton.getInstance() was also difficult to test because instead of asking, they are querying their dependency. Dependency injection becomes
An Example of why Singleton is Anti Pattern?
Here is an example of why the Singleton pattern is not good for modern Java development. You will see that testing a Singleton is almost impossible by writing unit tests.package patterns;
/*
* A class which uses Singleton class, MailService
*/
public class MailServiceClient {
/*
* This method is hard to test because it uses Singleton class.
*/
public void sendPaymentReminder() {
Singleton.getInstance().mail();
}
}
/*
* An eager implementation of Singleton pattern in Java.
*/
class MailService {
private static MailService INSTANCE = new MailService();
private MailService() {
throw new UnsupportedOperationException("Singleton
INSTANCE creation is not allowed");
}
public static MailService getInstance() {
return INSTANCE;
}
public void mail() {
System.out.println("Mail sent to external party");
}
}
Testing Singleton in Java
Now, let's try to write a test for Singleton class and find out how hard or easy it is:package patterns;
import static org.junit.Assert.*;
import org.junit.Test;
public class SingletonClientTest {
@Test
public void sendPaymentReminder() {
// $*@&#$ (how do I mock the Singleton
}
What is a better Alternative of Singleton Pattern in Java?
The alternative of Singleton using Interface and Dependency Injection as shown below, they will make your code easier to test as you can always pass a Mock or Dummy object for testing.
Now let's rewrite the SingletonClient class, which make use of the Singleton pattern using Dependency injection :
1. Hard to test
2. Hidden Coupling
3. Many instances of Singleton
4. Hard to Evaluate
5. Hard to SubClass
6. Better Alternatives available using Dependency Injection
7. Initialization order
- Reduce hidden coupling
- Allow testability
- Allow subclassing
- Make construction and use flexible
public interface MailService {
public void mail();
}
public class MailServiceImpl implements MailService{
public void mail(){
// send mail to external party
}
}
Now let's rewrite the SingletonClient class, which make use of the Singleton pattern using Dependency injection :
public class PaymentReminder{
private final MailService _mailService;
public PaymentReminder(MailService singleton){
this._mailService = singleton;
}
public void sendPaymentReminder(){
_mailService.mail(); // mail sent to external party
}
}
Now let's write a unit test to test our PaymentRemider class, this time. We don't need to mock Singleton. Instead, we will use dependency injection to inject a MockMailService, which doesn't send emails to the internal party.public class TestPaymentReminder{
MailService mock = new MockMailService();
PaymentReminder pr = new PaymentReminder(mock);
pr.sendPaymentReminder(); // mail will go to local INBOX
}
So, you can see that Dependency Injection offers a better alternative to Singleton Pattern in Java.
7 Reasons Why Singleton is Anti Pattern
Now, let's revise all the reasons why Singleton is Anti-pattern now and why Java developer should avoid it.
2. Hidden Coupling
3. Many instances of Singleton
4. Hard to Evaluate
5. Hard to SubClass
6. Better Alternatives available using Dependency Injection
7. Initialization order
What have we learned from Singleton?
Interfaces are Dependency injection provides a better alternative of Singleton:- Reduce hidden coupling
- Allow testability
- Allow subclassing
- Make construction and use flexible
That's all about why Singleton has now become an Anti Pattern in the era of Unit testing and DevOps. If you just need one instance, control by configuration, not by pattern, and this can be easily achieved by using modern-day DI and IOC frameworks like Spring or Google Guice. In the worst case, you can even do it by hand using setter injection.
Other Java Design Patterns tutorials you may like
Thanks a lot for reading this article so far. If you like this Java design pattern tutorial, then please share it with your friends and colleagues. If you have any questions or feedback, then please drop a note. You can also see these design pattern courses to learn more.
- 5 Free Courses to learn Object Oriented Programming (courses)
- How to implement Command Pattern in Java? (example)
- Difference between Factory and Dependency Injection Pattern? (answer)
- 7 Best Courses to learn Design Pattern in Java (courses)
- How to create thread-safe Singleton in Java (example)
- 7 Best Books to learn the Design Pattern in Java? (books)
- How to implement the Strategy Design Pattern in Java? (example)
- Difference between Factory and AbstractFactory Pattern? (example)
- How to implement Composite Pattern in Java? (example)
- 18 Java Design Pattern Interview Questions with Answers (list)
- How to design a Vending Machine in Java? (questions)
- 20 System Design Interview Questions (list)
- Difference between State and Strategy Design Pattern in Java? (answer)
- Top 5 Courses to learn Design Patterns in Java (courses)
- 5 Free Courses to learn Data Structure and Algorithms (courses)
Thanks a lot for reading this article so far. If you like this Java design pattern tutorial, then please share it with your friends and colleagues. If you have any questions or feedback, then please drop a note. You can also see these design pattern courses to learn more.
I'll agree using dependency injection does make life easier (for the reasons you mention), but something still needs to manage the lifecycle (creation/destruction) of the MailService and inject it where it needs to be injected correct? Wouldn't MailService's implementation still be a Singleton, even if it does implement an interface? You wouldn't want multiple instances of it in your application.
ReplyDeleteDependency injection frameworks (CDI/Spring/etc) for the most part treat beans as singletons, so I think saying "Singletons are bad" or "Singletons are an anti-pattern" isn't at all correct. Just using them properly so they provide the benefits you describe might be better.
Also, your code for the MailService singleton wouldn't actually work - the application would crash at startup. The private constructor throws UnsupportedOperationException. Just because the constructor is private doesn't mean it isn't called. Its called by private static MailService INSTANCE = new MailService();