19

I'm currently writing an MVC application and I need a class that can:

A: get, add and remove data(specifically a TreeSet of sorted strings that I want stored in memory, but I doubt the data itself is relevant.)

B: The data must be accessible to multiple controllers.

C: There can "presumably" only be one instance of this class. These controllers will often share this set.

D: Since the class will be accessed by multiple controllers, the class must be thread-safe.

It seems like an enum singleton would work for this problem but I've read that singletons are an anti-pattern and I'm curious as to possible alternatives?

I was thinking of maybe abstracting over this by creating one or multiple "API" classes that can modify the state of a single non-global instance(protected singleton?) containing our data and then passing these API classes into their respective controllers via dependency injection, but I feel like this may be kicking the can down the road? I genuinely don't know.

6 Answers6

30

A singleton can mean two things:

  1. An object where only a single instance exist

  2. A particular pattern (from the infamous Design Patterns book) for accessing singletons through a static method on class.

Singleton in sense (1) are fine and useful. Pattern (2) is problematic because this is really a global variable with extra steps, and this has well-known problems for testing, modularization and maintainability.

An alternative to (2) would be dependency injection.

JacquesB
  • 61,955
  • 21
  • 135
  • 189
17

I feel like the hate for the Singleton pattern is a little over-the-top. Yes, it can introduce some challenges, but it can also be a very simple solution in narrow circumstances. I think some of the invalid reasons for its bad name are:

  • Overwrought, unecessary, and broken implementations such as double-checked locking
  • Overuse. When Design Patterns were really hot, a lot of people learned exactly one pattern (Singleton) and used it all over the place where it wasn't appropriate at all.
  • Misunderstanding. Singleton, as typically implemented, is a simplification of the GoF pattern. E.g., the pattern shows a polymorphic implementation as an example.

That said, there are real issues. The main issue is testing. The standard Singleton (i.e., as typically written) is not easily replaceable with mocks or fakes in languages like Java.

The other primary issue is that if your assumption that there will never need to be more than one instance is wrong, or is no longer true in a future version, reworking the code to eliminate or enhance it can be challenging. Some of this can be mitigated by adding a (perhaps optional) parameter to your getInstance method and making it a 'multiton'.

My personal take is that the larger and more complex the application, the more problematic the approach becomes, and you should be using DI (with or without a framework.) If your application is relatively small and focused, introducing DI can be overkill. As long as you can accept the potential cost of reworking the code and your testing needs are met, I don't think it's the design kryptonite that it's usually made out to be.

JimmyJames supports Canada
  • 30,578
  • 3
  • 59
  • 108
6

The singleton pattern, as described in the gang of four book, as it is mostly implemented in Java, can indeed be considered an antipattern. This includes static accessors, enum singletons, and so on, which are at most singletons in the scope of a single class loader.

However, if you write Java software today, you'll probably not start out with public static void main() and roll your own from there. Usually, you'll be using some kind of container to perform the setup of your application, and there is your alternative. In CDI, you declare a bean as @ApplicationScoped, and there's a similar scope in Spring.

Still a singleton, but not a roll-your-own implementation. And this is quite OKish, provided that you are prepared to deal with global state, which this still represents.

Note however, that this kind of singleon is still "once per container", so the setup is not scalable. If you intend to run multiple instances of your application together in a cluster, you need to find a different solution.

mtj
  • 2,360
5

When I was first hired out of college, I started working with some software in the mil-sim sphere that is over 20 years old. Often times I was given a task and told to look at similar code in the program and emulate how it was coded in other parts, and build what they wanted the way it was already done. I would pour over dozens of classes and compare how each of them worked to come up with similar structures, and I saw everything from reflection, to factory of factories, to singletons.

When I finally decided on a solution, I would take it to one of my seniors and propose it to them. I remember the first time I suggested a singleton though. It was a reaction of dismay. Despite every single example I could find for what I wanted to do being in this pattern, my senior didn't like it. I didn't know why. We discussed it with him asking me why I wanted to use the pattern, and what I wanted to do. Eventually he realized that what I really needed was a single class only ever being instantiated once, and this pattern actually fit the bill. He signed off on it. He never explained why he found the concept so abhorrent.

I had many patterns in that code base that I learned over time to be wary of. Either I had to think and make sure that it was exactly what I needed, and had to learn why I would need to use that pattern, or I learned that the patterns in the code were being used wrongly and I needed a new pattern.

I don't think singletons are an antipattern. They can be difficult to work with and test, but if you need exactly what it provides, then you need a singleton. Nothing else will suffice. How you implement it, that's up to you. Though I've only learned the 20 year old one right now, I'm not above learning something new if it's better. Just remember, make sure when you make a singleton that you will never, ever, need more than one of the class. If you think you might need several, an object pool might be better, or just find a different pattern.

3

While there are many situations where using a singleton cache may be more convenient than having to maintain and pass around a caching context, many situations may arise where such designs end up creating difficulties. If the purpose of a cache is to store things that are by nature genuinely immutable (e.g. numbers and their cube roots), and the cache does not allow outside observation of its state, then having the cache be a singleton would be unlikely to cause any trouble. If, however, a cache is meant to be attached to some mutable entity and track its state, then having the cache be a singleton will effectively compel the observed object to be a singleton as well.

supercat
  • 8,629
0

No, singletons are not an anti-pattern, per se. Their function depends a bit on the programming language, especially on which features it provides to solve the problem of having a single instance of something around.

For all intents and purposes, true global state is an anti-pattern in almost all circumstances (exceptions may include things like embedded programs, quick throw-away "tooling"-style shell scripts and the like).

Singletons then allow you to get the features of global state without the drawbacks of global state. Sometimes your requirements absolutely do call for something that is around only once, and in a guaranteed manner. Singletons do that. They also - compared to global state - allow you to tightly control how that state can be instantiated, accessed and modified. All very useful properties.

In your case - static configuration - I see nothing wrong with it (it basically does not matter much). Also with dynamic (user-changeable) configuration. If your app supports that the user modifies a config file and automatically reloads that, then you would much rather have only a single time of reloading in your app, and not 50 classes having to reload their individual instances of your AppConfig class. This becomes ever more important the more expensive it is to work with the data (think DB accesses and so on and forth...).

As mentioned elsewhere, there may or may not be issues you have to solve; like how to exactly inject (instantiate) the singleton content, or what to do during testing. This depends on your general architectural choices, your preferences and so on and forth.

---- Rant on patterns ----

If you read that singletons are an anti-pattern, point blank, with no context, then that is rubbish. There are many abstract patterns out there, there are whole books solely discussing this topic (Design Patterns comes to mind).

It is an error to assume that the goal is to try hard to apply patterns wherever possible. Some devs can hardly write a class without applying three patterns of some kind or another on it before starting the first real line of code. This is a fallacy of course.

Patterns each solve a problem - you must first figure out if you have the problem at all, before turning to them. Knowing the most important patterns is still a very great thing for a dev: you can recognize them if you see them, and if your code calls for it, you already know about them, and can use them without coming up with some ad-hoc solution that's maybe not the greatest.

Also, these days we know how important it is to be able to refactor your code ruthlessly. It is absolutely fine to start out with a very direct, pattern-less implementation, and refactor later when you see that you are trying to repeat yourself, or if the codes starts to get unwieldy.

Using patterns for their own sake is like being a woodworker and trying to find a way to use a hammer for every single step of your project. You will hammer in screws; use your hammer to split boards, and so on and forth. This is bad of course. The good way is to have the hammer, and knows where it hangs, and be skilled in applying it when you need it.

AnoE
  • 5,874
  • 1
  • 16
  • 17