Time for chapter three of our Effective Java review. Today's is a fairly simple one. Today we are talking about the singleton pattern. The singleton pattern is quite well known and basically comes down to an object that only allows instantiation once.
So what benefits does a singleton give us?
- Expensive objects can be avoided being generated multiple times.
- If there is a reason we shouldn't have an unbounded amount of objects we can own instance control.
What about some cons:
- They are extremely hard to test (having your singleton implement an interface can help this some)
- They basically serve as global state that can be complex and cause bugs.
So a singleton is definitely not without it's costs but let's say we have determined that we do need a singleton, what are our options of implementing the pattern? Well Effective Java goes through three methods all including hiding the constructor of the object in one way or another.
Option 1: public constant field
The first option that is described is quite simple. The first step is to create a private constructor. After that you simply create a public static final field that instantiates the object to be used. Let's look at an example:
public class CEO {
public static final INSTANCE = new CEO();
private CEO() { }
public void fire(Employee slacker) { ... }
}
So in this example we want to make sure that we don't end up with more than one CEO and so we make a singleton out of class. Then when a user wants to gain access to the CEO they simple call CEO.INSTANCE
. This method is definitely very easy to write and very easy to use. The downsides are that users can get around the single instance protection by using some reflection skills. It also doesn't give as much control over instance creation and going back on the singleton choice.
Option 2: getInstance
factory method
The second option is what I am most familiar with. This method is built around having a static factory getInstance
factory method method that provides the single instance of the class. Example time:
public class CEO {
private static final INSTANCE = new CEO();
private CEO() { }
public static CEO getInstance() {
return INSTANCE;
}
public void fire(Employee slacker) { ... }
}
So what benefits do we get here? Well for one it's very recognizable as a singleton so the user can easily see what they are interacting with. It also allows us more control over our instance creation. For example we can delay instantiation of the object until it's first needed.
public CEO {
private static final INSTANCE;
public static synchronized CEO getInstance() {
if(INSTANCE == null) {
INSTANCE = new CEO();
}
return INSTANCE;
}
}
With a pattern like this you need to be careful about race conditions though. It is of note that this pattern is still susceptible to reflection attacks. Effective Java also goes into how to handle serialization as it relates to singletons and is not extremely straightforward. I've never had to deal with that so I won't tackle it here but if you need to go look it up.
Option 3: Single Element Enum
The final option is to create a single element enum. This was not an option that I had ever seen and it does still feel a little unnatural to me. So let's look at an example:
public enum CEO {
INSTANCE;
public void fire(Employee slacker) { ... }
}
So what does this give us? Well it is simple to write. Probably the least code of the three options. It also is very safe. It doesn't leave the option for reflection attacks. It also handles the serialization complexities that I skipped above. It does make it so you can't extend from a class but you can still implement interfaces. Effective Java pitches that this is the option that most people should take. Given that I have never personally seen someone use this method it's quite interesting to me that it is pitched as the option most should take.
How about you? Have you ever seen this method used?
Top comments (14)
I know I'm late to the party, but I wonder why you would ever use the Singleton pattern instead of just a static class. Doesn't it achieve exactly the same?
I'm not sure I'm following what you mean by "static class." The only static classes come to mind are static inner classes (dev.to/kylec32/effective-java-favo...) which don't solve the singleton problem but instead allows access to a nested class without having a handle on the outer class. Still useful, just not for this problem. Maybe I'm just misunderstanding the question thought.
Thank you for your response. I guess I didn't explain very well. What I mean is a class where the members and methods are static. This way you can call the static method of the class to do what you want instead of creating an object. eg: ClassName.staticMethod()
Thanks for that clarification. Yeah I don't see any problem with that. It seems like just a slight change from option 1. Potentially the downside I see is you don't keep the option of changing how it gets initialized in the future. If that's not needed then you likely are good with this option.
Thank you for your answer!
What about storing the singleton value in a static nested class?
Interesting. I see no reason that you couldn't do that if it made sense to put the singleton in such a nested class. Something to keep in mind if the option ever comes up.
I thought that was the official way to create lazy singletons without synchronization.
Will you look at that! Still never seen that but you are 100% correct this would allow lazy initialization without synchronization. Thanks for educating me about that.
Wow.. this is pretty awesome series. Gonna bookmark all the posts in this thread/series and start grasping them gradually. Thank you Kyle.
"They are extremely hard to test" - why?
A fair question indeed, and one that I did not demonstrate above in my examples. Since the constructor is not accessible to us, even in a test, we are unable to change how the object is created and can't get the benefit of dependency injection (dev.to/kylec32/effective-java-tues...). While none of the above examples show any places where that would be a problem, many singletons in the wild do have this problem if they have any external dependencies (DB, network, other components, etc). So the more I think of it, it's likely the lack of ability to use dependency injection that is the true problem here.
Compare that to a singleton that is just a collection of pure functions that would indeed be testable (but at that point you are more looking at a Utility class dev.to/kylec32/effective-java-tues...)
I think you're right about Dependency Injection for unit testing with only, say, JUnit 5.
However, this is where I have found mocking libraries such as Mockito benefit the tests, as the coupled logic can be stubbed out.
(Thanks for the detailed reply đź‘Ť )
Edit: Your second link doesn't appear to be working?
Per Effective Java - we get the serialization machinery free.