Home » So Singletons are bad, then what?

So Singletons are bad, then what?

Solutons:


It’s important to distinguish here between single instances and the Singleton design pattern.

Single instances are simply a reality. Most apps are only designed to work with one configuration at a time, one UI at a time, one file system at a time, and so on. If there’s a lot of state or data to be maintained, then certainly you would want to have just one instance and keep it alive as long as possible.

The Singleton design pattern is a very specific type of single instance, specifically one that is:

  • Accessible via a global, static instance field;
  • Created either on program initialization or upon first access;
  • No public constructor (cannot instantiate directly);
  • Never explicitly freed (implicitly freed on program termination).

It is because of this specific design choice that the pattern introduces several potential long-term problems:

  • Inability to use abstract or interface classes;
  • Inability to subclass;
  • High coupling across the application (difficult to modify);
  • Difficult to test (can’t fake/mock in unit tests);
  • Difficult to parallelize in the case of mutable state (requires extensive locking);
  • and so on.

None of these symptoms are actually endemic to single instances, just the Singleton pattern.

What can you do instead? Simply don’t use the Singleton pattern.

Quoting from the question:

The idea was to have this one place in the app which keeps the data stored and synced, and then any new screens that are opened can just query most of what they need from there, without making repetitive requests for various supporting data from the server. Constantly requesting to the server would take too much bandwidth – and I’m talking thousands of dollars extra Internet bills per week, so that was unacceptable.

This concept has a name, as you sort of hint at but sound uncertain of. It’s called a cache. If you want to get fancy you can call it an “offline cache” or just an offline copy of remote data.

A cache does not need to be a singleton. It may need to be a single instance if you want to avoid fetching the same data for multiple cache instances; but that does not mean you actually have to expose everything to everyone.

The first thing I’d do is separate out the different functional areas of the cache into separate interfaces. For example, let’s say you were making the world’s worst YouTube clone based on Microsoft Access:

                          MSAccessCache
                                ▲
                                |
              +-----------------+-----------------+
              |                 |                 |
         IMediaCache      IProfileCache      IPageCache
              |                 |                 |
              |                 |                 |
          VideoPage       MyAccountPage     MostPopularPage

Here you have several interfaces describing the specific types of data a particular class might need access to – media, user profiles, and static pages (like the front page). All of that is implemented by one mega-cache, but you design your individual classes to accept the interfaces instead, so they don’t care what kind of an instance they have. You initialize the physical instance once, when your program starts, and then just start passing around the instances (cast to a particular interface type) via constructors and public properties.

This is called Dependency Injection, by the way; you don’t need to use Spring or any special IoC container, just so long as your general class design accepts its dependencies from the caller instead of instantiating them on its own or referencing global state.

Why should you use the interface-based design? Three reasons:

  1. It makes the code easier to read; you can clearly understand from the interfaces exactly what data the dependent classes depend on.

  2. If and when you realize that Microsoft Access wasn’t the best choice for a data back-end, you can replace it with something better – let’s say SQL Server.

  3. If and when you realize that SQL Server isn’t the best choice for media specifically, you can break up your implementation without affecting any other part of the system. That is where the real power of abstraction comes in.

If you want to take it one step further then you can use an IoC container (DI framework) like Spring (Java) or Unity (.NET). Almost every DI framework will do its own lifetime management and specifically allow you to define a particular service as a single instance (often calling it “singleton”, but that’s only for familiarity). Basically these frameworks save you most of the monkey work of manually passing around instances, but they are not strictly necessary. You do not need any special tools in order to implement this design.

For the sake of completeness, I should point out that the design above is really not ideal either. When you are dealing with a cache (as you are), you should actually have an entirely separate layer. In other words, a design like this one:

                                                        +--IMediaRepository
                                                        |
                          Cache (Generic)---------------+--IProfileRepository
                                ▲                       |
                                |                       +--IPageRepository
              +-----------------+-----------------+
              |                 |                 |
         IMediaCache      IProfileCache      IPageCache
              |                 |                 |
              |                 |                 |
          VideoPage       MyAccountPage     MostPopularPage

The benefit of this is that you never even need to break up your Cache instance if you decide to refactor; you can change how Media is stored simply by feeding it an alternate implementation of IMediaRepository. If you think about how this fits together, you will see that it still only ever creates one physical instance of a cache, so you never need to be fetching the same data twice.

None of this is to say that every single piece of software in the world needs to be architected to these exacting standards of high cohesion and loose coupling; it depends on the size and scope of the project, your team, your budget, deadlines, etc. But if you’re asking what the best design is (to use in place of a singleton), then this is it.

P.S. As others have stated, it’s probably not the best idea for the dependent classes to be aware that they are using a cache – that is an implementation detail they simply should never care about. That being said, the overall architecture would still look very similar to what’s pictured above, you just wouldn’t refer to the individual interfaces as Caches. Instead you’d name them Services or something similar.

In the case you give, it sounds like the use of a Singleton is not the problem, but the symptom of a problem – a larger, architectural problem.

Why are the screens querying the cache object for data? Caching should be transparent to the client. There should be an appropriate abstraction for providing the data, and the implementation of that abstraction might utilize caching.

The issue is likely that dependencies between parts of the system are not set up correctly, and this is probably systemic.

Why do the screens need to have knowledge of where they get their data? Why are the screens not provided with an object that can fulfill their requests for data (behind which a cache is hidden)? Oftentimes the responsibility for creating screens is not centralized, and so there is no clear point of injecting the dependencies.

Again, we are looking at large-scale architectural and design issues.

Also, it is very important to understand that the lifetime of an object can be completely divorced from how the object is found for use.

A cache will have to live throughout the application’s lifetime (to be useful), so that object’s lifetime is that of a Singleton.

But the problem with Singleton (at least the common implementation of Singleton as a static class/property), is how other classes that use it go about finding it.

With a static Singleton implementation, the convention is to simply use it wherever needed. But that completely hides the dependency and tightly couples the two classes.

If we provide the dependency to the class, that dependency is explicit and all the consuming class needs to have knowledge of is the contract available for it to use.

I wrote a whole chapter on just this question. Mostly in the context of games, but most of it should apply outside of games.

tl;dr:

The Gang of Four Singleton pattern does two things: give you convenience access to an object from anywhere, and ensure that only one instance of it can be created. 99% of the time, all you care about is the first half of that, and carting along the second half to get it adds unnecessary limitation.

Not only that, but there are better solutions for giving convenient access. Making an object global is the nuclear option for solving that, and makes it way to easy to destroy your encapsulation. Everything that’s bad about globals applies completely to singletons.

If you’re using it just because you have a lot of places in code that need to touch the same object, try to find a better way to give it to just those objects without exposing it to the entire codebase. Other solutions:

  • Ditch it entirely. I’ve seen lots of singleton classes that don’t have any state and are just bags of helper functions. Those don’t need an instance at all. Just make them static functions, or move them into one of the classes that the function takes as an argument. You wouldn’t need a special Math class if you could just do 123.Abs().

  • Pass it around. The simple solution if a method needs some other object is to just pass it in. There’s nothing wrong with passing some objects around.

  • Put it in the base class. If you have a lot of classes that all need access to some special object, and they share a base class, you can make that object a member on the base. When you construct it, pass in the object. Now the derived objects can all get it when they need it. If you make it protected, you ensure the object still stays encapsulated.

Related Solutions

How to generate a random string?

My favorite way to do it is by using /dev/urandom together with tr to delete unwanted characters. For instance, to get only digits and letters: tr -dc A-Za-z0-9 </dev/urandom | head -c 13 ; echo '' Alternatively, to include more characters from the OWASP...

How to copy a file from a remote server to a local machine?

The syntax for scp is: If you are on the computer from which you want to send file to a remote computer: scp /file/to/send username@remote:/where/to/put Here the remote can be a FQDN or an IP address. On the other hand if you are on the computer wanting to...

What is the difference between curl and wget?

The main differences are: wget's major strong side compared to curl is its ability to download recursively. wget is command line only. There's no lib or anything, but curl's features are powered by libcurl. curl supports FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP,...

Using ‘sed’ to find and replace [duplicate]

sed is the stream editor, in that you can use | (pipe) to send standard streams (STDIN and STDOUT specifically) through sed and alter them programmatically on the fly, making it a handy tool in the Unix philosophy tradition; but can edit files directly, too,...

How do I loop through only directories in bash?

You can specify a slash at the end to match only directories: for d in */ ; do echo "$d" done If you want to exclude symlinks, use a test to continue the loop if the current entry is a link. You need to remove the trailing slash from the name in order for -L to...

How to clear journalctl

The self maintenance method is to vacuum the logs by size or time. Retain only the past two days: journalctl --vacuum-time=2d Retain only the past 500 MB: journalctl --vacuum-size=500M man journalctl for more information. You don't typically clear the journal...

How can I run a command which will survive terminal close?

One of the following 2 should work: $ nohup redshift & or $ redshift & $ disown See the following for a bit more information on how this works: man nohup help disown Difference between nohup, disown and & (be sure to read the comments too) If your...

Get exit status of process that’s piped to another

bash and zsh have an array variable that holds the exit status of each element (command) of the last pipeline executed by the shell. If you are using bash, the array is called PIPESTATUS (case matters!) and the array indicies start at zero: $ false | true $...

Execute vs Read bit. How do directory permissions in Linux work?

When applying permissions to directories on Linux, the permission bits have different meanings than on regular files. The read bit (r) allows the affected user to list the files within the directory The write bit (w) allows the affected user to create, rename,...

What are the pros and cons of Vim and Emacs? [closed]

I use both, although if I had to choose one, I know which one I would pick. Still, I'll try to make an objective comparison on a few issues. Available everywhere? If you're a professional system administrator who works with Unix systems, or a power user on...

How do I use pushd and popd commands?

pushd, popd, and dirs are shell builtins which allow you manipulate the directory stack. This can be used to change directories but return to the directory from which you came. For example start up with the following directories: $ pwd /home/saml/somedir $ ls...

How to forward X over SSH to run graphics applications remotely?

X11 forwarding needs to be enabled on both the client side and the server side. On the client side, the -X (capital X) option to ssh enables X11 forwarding, and you can make this the default (for all connections or for a specific connection) with ForwardX11 yes...

What does “LC_ALL=C” do?

LC_ALL is the environment variable that overrides all the other localisation settings (except $LANGUAGE under some circumstances). Different aspects of localisations (like the thousand separator or decimal point character, character set, sorting order, month,...

What is a bind mount?

What is a bind mount? A bind mount is an alternate view of a directory tree. Classically, mounting creates a view of a storage device as a directory tree. A bind mount instead takes an existing directory tree and replicates it under a different point. The...

Turn off buffering in pipe

Another way to skin this cat is to use the stdbuf program, which is part of the GNU Coreutils (FreeBSD also has its own one). stdbuf -i0 -o0 -e0 command This turns off buffering completely for input, output and error. For some applications, line buffering may...

Can less retain colored output?

Use: git diff --color=always | less -r --color=always is there to tell git to output color codes even if the output is a pipe (not a tty). And -r is there to tell less to interpret those color codes and other escape sequences. Use -R for ANSI color codes only....

How do I copy a folder keeping owners and permissions intact?

sudo cp -rp /home/my_home /media/backup/my_home From cp manpage: -p same as --preserve=mode,ownership,timestamps --preserve[=ATTR_LIST] preserve the specified attributes (default: mode,ownership,timestamps), if possible additional attributes: context, links,...

Can I zip an entire folder using gzip?

No. Unlike zip, gzip functions as a compression algorithm only. Because of various reasons some of which hearken back to the era of tape drives, Unix uses a program named tar to archive data, which can then be compressed with a compression program like gzip,...

How do I check package version using apt-get / aptitude?

apt-get You can run a simulation to see what would happen if you upgrade/install a package: apt-get -s install <package> To see all possible upgrades, run a upgrade in verbose mode and (to be safe) with simulation, press n to cancel: apt-get -V -s upgrade...