How we implement Singletons in Scala at dataxu

Javier Buquet
dataxu.technology

--

At dataxu, we have been using Scala for a while now, and in our journey we have perfected our best practices. Among these best practices, we have refined our implementation of the Singleton design pattern in Scala. In implementing this pattern there are some aspects that require significant attention to detail to increase its ease of use, especially when testing.

The Singleton design pattern, according to Wikipedia, is a software design pattern that restricts the instantiation of a class to one object. This means that this pattern promises to be handy when dealing with a class that we want to instantiate once, and only once, across a system (or, more generally, a subsystem), and have that same instance shared by every potential user of the class. For example, think about a DatabaseConnection class that handles the connection between the system and its database, it is reasonable to have only one instance of the class and make any database client use that same connection.

But the question is, how do we implement this Singleton design pattern in Scala?

Implementing the Singleton design pattern in Scala

If you have already given Scala a try, you are probably familiar with Scala’s Singleton Objects. These are Scala’s approach to the static keyword in Java. Any function or variable defined in a Scala Object can be used statically, as there is one and only one instance of an object, which is not instantiated by any consumer, but just exists.

Our first hunch when implementing the Singleton design pattern in Scala was to make use of the Singleton Objects.

Let’s say we actually want to implement the DatabaseConnection class we mentioned above following the Singleton design pattern. This will allow different consumers to share the same DatabaseConnection instance for running commands on our application’s database. Using Scala’s Singleton Objects we can achieve this by doing:

And that’s all. We can now support different consumers running commands on a shared DatabaseConnection instance implementing the Singleton design pattern. For example, we could have a function somewhere else that retrieves all the Users in the database, as follows:

As you can see, there’s no need to instantiate a new DatabaseConnection nor to somehow keep a reference to it — we have direct access to the one and only DatabaseConnection instance just as if it was a static call in Java.

As we started using this approach in our code, we found it had some limitations that were important to note:

  • There’s no way one can easily swap the implementation being used. The fact that the user does not control the creation of this singleton instance — as it exists naturally in the program — makes it impossible to easily swap the implementation being used. It is common for us at dataxu to need a different implementation of our DatabaseConnection class when running our unit tests so that we don’t connect to the actual database but instead use an in-memory implementation of the class which is faster and cheaper. Using the Singleton Object does not allow us to swap the implementation of DatabaseConnection we want to instantiate as the singleton, but instead we would need to modify each and every consumer of the class to point to a different Object.
  • Can’t be instantiated lazily. It might be the case that we need our singleton instance to be created lazily — only instantiate it once it has been requested for the first time by a consumer, not before. Again, as we don’t control the creation of this singleton instance, there’s no way we can do so lazily — it will be instantiated upon program execution.

These limitations are a consequence of the fact that these Scala Singleton Objects are not meant to be used this way. These Objects are not a built-in implementation of the Singleton design pattern, but instead are Scala’s approach to Java’s static. Hence, we should not include in these Objects code that is intended to be used as non-static code accessed through a singleton instance, but instead we should only include static code.

Perfecting our implementation of the Singleton design pattern

Given the limitations of our first approach, we came up with a different strategy that could effectively implement the Singleton design pattern without sacrificing the ease to test and use, while still being clean and elegant.

We knew that we shouldn’t use a Scala Object, as it proved to not fit our needs. Instead, we should use a regular Scala Class and have some infrastructure for holding a single instance of it and making sure we only allow that one instance to be used.

By the way, this is similar to how we implement the Singleton design pattern in Java — we define a class and its functionality, and add static infrastructure for handling the singleton instance. In this case, Scala helps to make our code cleaner by more explicitly encapsulating the static code in a Scala Object.

Our code now looks like this:

And we can use the singleton instance this way:

And voilà — we can now easily change the implementation of DatabaseConnection we want to instantiate as the singleton instance by just modifying the instantiation of the val instance in our object DatabaseConnection, as well as instantiating the singleton instance lazily just by adding the lazy keyword before the val for the instance.

This infrastructure can easily (and we think it should) be extended to allow programmatic swapping of the singleton instance being held. This means adding support for overriding the implementation being used, using a single line of code, which comes in very handy for solving the case mentioned above where we want to use a different implementation of the DatabaseConnection class when testing.

We decided to add this infrastructure to new functions to make our lives easier when testing:

  • A force(…) function that receives an instance of DatabaseConnection and forces the singleton instance to be the one received.
  • A reset() function that puts the singleton instance back to its default (not-forced) one.

Generalizing our solution

Once we were happy with our solution, and proved that it was an effective implementation of the Singleton design pattern in Scala, we tried to generalize our solution to make it as simple as possible for other classes using this infrastructure. We came up with a simple utility that any developer who needs to implement a Singleton class in Scala can simply extend and start using.

And can be simply used as follows:

We also created a test base for our solution so that those who are using it can very easily have their Singleton Instance Providers thoroughly tested, just by extending this trait in their test classes.

What do you think of our solution? Do you think others would find it useful if we published this simple utility? How do you handle implementing Singletons in Scala? Please let us know! If you found this post useful, please feel free to “applause” and share!

--

--

Javier Buquet
dataxu.technology

Software Engineer. Data Engineering @ Spotify - Scala, Python, Spark, Big Data, Clean Code, and more