This is a mobile version, full one is here.
Yegor Bugayenko
27 June 2016
Singletons Must Die
I think it’s too obvious to say that a singleton is an anti-pattern as there are tons of articles about that (singleton being an anti-pattern). However, more often than not, the question is how to define global things without a singleton; and the answer to that is not obvious for many of us. There are several examples: a database connection pool, a repository, a configuration map, etc. They all naturally seem to be “global”; but what do we do with them?
I assume you already know what a singleton is and why it’s an anti-pattern. If not, I recommend you read this Stack Overflow thread: What is so bad about singletons?
Now that we agree it’s a bad deal, what do we do if we need to, let’s say, have access to a database connection pool in many different places within the application? We simply need something like this:
class Database {
public static Database INSTANCE = new Database();
private Database() {
// Create a connection pool
}
public java.sql.Connection connect() {
// Get a new connection from the pool
// and return it
}
}
Later in at, say, the JAX-RS REST method, we need to retrieve something from the database:
@Path("/")
class Index {
@GET
public String text() {
java.sql.Connection connection =
Database.INSTANCE.connect();
return new JdbcSession(connection)
.sql("SELECT text FROM table")
.fetch(new SingleOutcome(String.class))
}
}
In case you’re not familiar with JAX-RS, it’s a simple
MVC architecture,
and this text()
method is a “controller.” Additionally, I’m using
JdbcSession
,
a simple JDBC wrapper from jcabi-jdbc.
We need that Database.INSTANCE
to be a singleton, right? We need it to
be globally available so that any MVC controller can have direct
access to it. Since we all understand and agree that a singleton is an evil
thing, what do we replace it with?
A dependency injection is the answer.
We need to make this database connection pool dependency of the controller
and ensure it’s provided through a constructor. However, in this particular
case, for JAX-RS, we can’t do it through a constructor thanks to its
ugly architecture. But we can create a ServletContextListener
,
instantiate a Database
in its contextInitialized()
method,
and add that instance as an attribute of
servletContext
. Then, inside
the controller, we retrieve the servlet context by adding the
javax.ws.rs.core.Context
annotation to a setter and using
getAttribute()
on it. This is absolutely terrible and procedural, but it’s better
than a singleton.
A proper object-oriented design would pass an instance of Database
to all objects that may need it through their constructors.
Nonetheless, what do we do if there are many dependencies? Do we make a 10-argument constructor? No, we don’t. If our objects really need 10 dependencies to do their work, we need to break them down into smaller ones.
That’s it. Forget about singletons; never use them. Turn them into dependencies
and pass them from object to object through the operator new
.
Do you use Singletons in your code? #elegantobjects
— Yegor Bugayenko (@yegor256) July 21, 2019