Why singleton design pattern is most popular, but not the most effective one

Callback design pattern

If you are building a system that involves usage of external resources, it is very easy to use singleton design pattern. If you check any medium-to-large scale project that you shipped, it will be quite easily visible. Be it for disk usage (DiskManager), Network access (NetworkManager), External Accessories (DeviceManager).

A simple search on your machine will reveal that below is the mostly used token in your codebase.

sharedInsance

While most handy one to create something on the fly that you only need once, singletons are not helpful when it comes to the most difficult design decisions using patterns: handling dependencies.

How singleton design pattern is implemented?

Here is a sample code snippet with singleton design pattern implemented using Java:

class Singleton { 
private static Singleton sharedInstance = null; static Singleton getInstance() {
if(sharedInstance == null)
sharedInstance = new Singleton();
return sharedInstance;
}
}

If you notice, single instance of Singleton is private. This means that external components can no longer know how it is instantiated. Any properties that it needs at runtime has to be passed by Singleton class itself. Hence it provides no inter-operability between objects.

That is precisely the reason singletons are used for things that need to encapsulate things unrelated to the rest of your application: network resources, disk usage, external device connections, database handling etc. They do not need anything from your application for their own construction, and their methods can be useful whenever parameter passing is required for operations. They are also quite fast when it comes to application development, because you do not need to make any design decision making before implementing them.

When singleton design pattern isn’t effective?

If you want to establish relationship between your entities (say, customer and accounts in a bank application), none of them can be a singleton.

And if you make them that way, it’s a design decision that you will surely regret. Just look at the figure below: both customer and account can have one-to-many relationships possible, and none of them will make viable singletons.

Singleton design pattern is not the master key

And here is how you fall into singleton trap: Your tech lead tells you to implement a system involving a customer and an account, with simple use-cases of deposit and withdrawal, without mentioning possibility of adding:

  • More customers to single account
  • More account to single customer

Who fixes the problems with singleton design pattern?

There exists a design pattern that solves problems attached with singletons: Dependency Injection. And in recent years it has gained quite some traction.

In reality, dependency injection is a 25-dollar term for a 5-cent subject. All it says is to define parameters that your objects absolutely needs – like customer needs its list of accounts, and vice versa.

But when it comes to rapid application development, dependency injection is not the most effective design pattern. Sure, it is beneficial to promote TDD. But if you want to quickly design multiple objects with need to communicate among each other, it is not the most effective design pattern.

Which is the most effective design pattern?

The most effective and easily understood design pattern is callbacks, implemented using blocks.

Experience

In fact, there is no such thing as callback pattern. It is an observer pattern implemented using function pointers.

  • Object A tells Object B to do some operation, and also tells it to notify itself using a callback function / block
  • Object B does the task, and notify Object A by calling this function / block (and optionally supplies some data)

Here is a small javascript snippet:

  var i = 1001;
setTimeout( function(){
console.log("Here is i:", i);
}, 500 );

setTimeout() is a javascript function that needs another function – to know what it should do after 500 milliseconds. The real beauty of it lies in the fact that you can pass value of i, your own variable to javascript library object implementing setTimeout() – all in a single place!

A node.js example that shows killer simplicity of a block:

var fs = require('fs');
fs.readFile(fileName, function(err, data) {
if(err) {
processError(err);
}
processFileData(data)
})

The design methodology of fs class has ensured that consumer of readFile() function can define what to do with the data that is read as string, or err object to report the error past the operation.

Whether you are coding javascript or Java or C#, that is also how you should design

  • Your network manager class. fetchURL should accept a function callback that takes URL response data (JSON) and error object.
  • Your database manager class. fetchDatafromDB should accept a function callback that takes array of objects and error object.

Conclusion:

Callback functions, when implemented as closures or blocks, help you implement observer pattern without switching between various function definitions. You can even use variables defined outside callback scope inside callback scope. As opposed to singleton design pattern that is most popular, it makes explicit dependencies between objects can be easily used in TDD as well.