Singletons versus Dependency Injection

Jordan Osterberg on August 18, 2017

Often I've been asked the question "What's the difference between Singletons and Dependency Injection?" or "Why should I use <insert access pa... [Read Full]
 

Main thing for me to choose dependency injection:

Testing. Isolation heavy services, external services. Impossible to test an order library if it is coupled with a braintree library. So add an abstraction layer and just inject.

Second reason would be implementation swapping. For example locally I just log emails, but on production I send them via API. Injection makes this soo easy, just swap out the implementation in your bootstrap code.

 

First of all, I think your example of DI is suboptimal, since the dependency (ExampleOne) creates the object it is a dependency for (ExampleTwo). This effectively couples the two things from one direction (ExampleOne cannot be used without ExampleTwo). A imho better example would be to wire the whole thing together in the main method by creating two completely independent instances of ExampleOne and ExampleTwo. Of course, this would require adding an interface, but that's a good best practice anyway.

And I agree with Mārtiņš, the main advantage of DI I see every day at work is testing. Being able to decouple the test from all the dependencies is great, you can really concentrate on the single unit instead of having to make sure all depencencies work, too.
With Singletons, you end up adding code to be able to switch out the singleton instance, watering down the whole concept.

In general, I would advise NOT using singletons in the classic sense, since yes, they are easy to access, but they tend to make more trouble than they solve in the long run. Also most dependency injection frameworks make it easy to only instantiate one instance (in Spring, this is even default behavior) and use that everywhere as a dependency.

This is perhaps another important point: Dependency Injection with a good framework for that makes it again easier and accessing a singleton in, for example, Spring Boot, is pretty much as easy as using a static method - only much more powerful.

 

In general, I would advise NOT using singletons in the classic sense

Singletons have their uses too, although I agree they're often overused (or simply abused).
But consider for example you know you always need exactly one instance, and you need all objects to use only the current instance, and you need it hot-swappable. Without using dependency injection libraries (which are not always available) it would be a pain to achieve this without singleton pattern.

As an example, let's say you're writing logging platform. you want to be able to switch logging system at any point. With singleton pattern you can just use Logger.getInstance().log(), or even Logger.log() and use singleton under the hood (that would prevent people from storing the result of getInstance). When you need to switch logging system, you just switch the instance, and all calls to Logger.log will now use that new instance. With dependency injection you would have to somehow notify and switch all classes with injected Logger instance.

 

No, you are thinking to complicated. With dependency injection, you can simply give out a delegator as the dependency that holds the actual Logger instance and delegates all calls to that instance. By switching the instance there all classes that got that delegator will use it. No problem with dependency injection there. And you can do nice stuff this way, too, for example...

Class X gets the Delegator but only as a Logger, so it can only log with it.
Class Y gets the Delegator but as another interface, let's call it LoggerDelegator, which also allows to use the setDelegate(...) method, which alle the other classes don't even see.

With a static singleton, that would be harder (you could try some smelly cast stuff, but with DI you do not even need that).

Personally, I believe that a static singleton is often the easiest way to code it, but I have yet to find a situation in which it is the only or even just the best way to do so. Sometimes it may be sufficient, true, then use it, as long as it makes no problem, but personally, I would start with DI, because in the long run, it's always more powerful and it is much easier to change things with DI later than changing the hardcoded Singleton.

Good point, didn't think of solving it like that.

In such a simple case though I'd probably start with singleton approach though, unless I knew beforehand I'll need something more ー simply because singleton is somewhat easier to code (no need for injecting in every class that might need it).

Unfortunately, I have made the experience, that many things start out as "just something easy" and get complicated very soon. While of course you have to keep YAGNI ("you aren't gonna need it") in mind, it's also dangerous to choose the most simple solution if it comes with a technical debt (which Singletons often do).

I agree with you here, but it's important to keep the balance between YAGNI and over-designing, lest you want to end up with something like Enterprise Fizz Buzz ( github.com/EnterpriseQualityCoding... - I'm not related to that project in any way, just found it amusing)

Another thing I noticed after looking through my recent projects is, sometimes due to external constrains you can't use dependency injection. I looked through my code from last year, and only places where I found singletons was in games written in Unity engine.
The thing is you need to bind objects created in the editor to the code somehow, and using Find* provided by unity is too inefficient. Another way to do it is to have some classes that have these objects assigned in the editor itself, but then editor handles creating instances of those classes, so there's no way to use dependency injection here - I just need to find the object that's already created. Using singleton here solves many problems that would otherwise arise.
As for why not creating everything from code, and just loading it: the scenes, prefabs, UI, etc. are created by designers who are not programmers. If a programmer had to create all that (+ a sane loading mechanism) it would take too much time to create even medium-sized projects.

 

I'm pro-statics and anti-unit testing, prefer integration testing for spot checking and automated testing for larger scopes.

 

Good luck finding a specific bug that does not result in an exception but simple wrong values with integration testing. With unit testing, it's simply reading the line that says "CalculaturClass: Expected x to be y, but was z" ;-) With integration testing you might end up knowing that somewhere in the end, the value is wrong, but where... Both kind of tests have their uses, none should be used exclusively.

 

An integration test can do the same thing, it would just be pulling the data from a real database rather than assuming returned values from a mocked data access object.

A unit test tests a unit and thus is per definition much more fine-grained than an integration test, where you cannot really separate multiple units from each other. In a unit test, you test ONE single unit and know if this single unit produces an error. In an integration test, you test multiple units together and all you can basically say is that this combination somehow produces an error. You do not automatically know which part of this combination is responsible for the error:

Is the db query bad? Is the db data bad? Something wrong the mapping? Or does the service calculate wrongly? No way of knowing easily, you will have to check all these possibilities.

A unit test for the service, on the other hand, can tell you immediately: "This service produces a wrong result.", no need to check anything else.

Besides that, unit tests tend to be a hell of a lot faster which makes it easier to run them pretty much all the time.

That sounds, to me, like an argument for integration testing - there's a long pipeline and if anything is wrong, then it's all wrong.

You don't necessarily need to assert at the end of your test, you can incrementally assert. Mid-test may throw an exception but it's easy enough to debug immediately afterwards.

I've found it very useful that integration tests also work great as load tests. At least, very easy to get something much more similar to real-world than a unit test would (in Visual Studio).

Running all the unit tests all the time without integration testing still leaves you without being fully tested, which is where CD-integrated automated testing becomes useful.

"then it's all wrong"... No, not really. What your test should tell you is not "Hey, there is an error, somewhere... Don't know there.". A good test should test one thing and then tell you what went wrong, by failing. Your integration test cannot do that, because it's a complicated thing with many components. Unit tests for the single components will save you a lot of trouble there, because they can pinpoint many errors exactly.

And the name "integration" test tells us, that we are the testing the integration of various components - but the components themselves should already be relatively bug-free. Integrating horribly buggy components and then getting "Meep, doesn't work" helps noone. Integration testing some components, each of which works exactly as specified, proven by unit tests, can actually concentrate on how well they work together, how well the integrate.

And of course, testing with various different data can (doesn't have to, but often is) a hassle with integration testing, since you have to setup the database, etc. With a unit test, I can fire a million values at my unit and check that all the results are correct easily and quickly and then concentrate on only testing the important test cases in the integration test.

And don't get my wrong: I do not advise using only unit tests, that would be stupid. Of course you also need integration tests (and others). I just do not believe that integration tests can do the same things as good as unit tests (and the other way round, unit tests cannot do the same things as integration tests).

I'm not sure what your experience with integration tests is, mine tell me exactly where the failure occurred. In my experience, integration tests are essentially the same thing as unit tests, just with no mocking. And mocking brings a heap of other work associated with it - everything needs an interface, dependency injection, instance containers and it becomes a significant amount of unnecessary overhead. Avoiding the whole lot makes the entire application simpler, understandable and more procedural.

A downside would be that another developer couldn't necessarily reuse the tests on his local without the same data source.

You don't need to have dependency injection, instance containers or even interfaces to make Unit Tests. You can test your entire system using Unit Tests with no access to the network and it will execute in terms of milliseconds. You will never have the amount of False Positive results that Integration Tests can have and your tests will never fail when any external dependency fail. Even an insert in the database can lead to false positive errors. And no need to have a lot of CI slaves and Selenium stuffs. What's the size of the projects that you work today? I have already worked in a project with ~2 million lines and zero Unit Tests, and have worked in a lot of projects with 90% of coverage. My liability increased a lot with Unit Tests. By the way, I don't know today how to program without Unit Tests.

I understand unit testing does not require mocking, yet mocking is required for unit test coverage of applications that integrate with external resources.

I don't understand your point about false positives. Can you give an example?

It's unacceptable for an external dependency to fail for my team. We expect 100% uptime.

Automated testing would be highly desirable regardless of unit/integration test preference to take load off of QA personnel.

Congratulations on your achievements.

 
 

WTF? How can you possibly compare such orthogonal concepts? Managing your dependencies is one of THE most important aspects of writing maintainable code.

Avoid using static objects, they will bite you back later in the project. They promote unnecessary strong coupling, hard to trace side effects and make testing difficult. Use singletons very sparingly.

Dependency injection is an essential concept that every engineer should learn (not to be confused with IoC containers, though closely related). I cannot state enough how important dependency management is for writing maintainable code.

While you're researching DI, you should also learn the other concepts in Uncle Bob Martin's SOLID principles. Learn them, use them, grok them. There are lots of articles on the topic, google it, or start with Wikipedia - en.wikipedia.org/wiki/SOLID_(objec....

 

One aspect of Singletons is the order of initialisation that is hard to track down, in case some changes in dependencies cause a change that breaks something.
This is true for lazy init inside getInstance and for direct assignment to static field, as (at least in Java if I remember correctly) the static part of the class is initialized when it is first accessed. (Even when all static classes would be initialized when loaded, what is the order?)

This is of course more relevant the more complex a code base is/ the more Singletons there are.

With DI you can have singeltons (lower case s) of every class you want, in as many scopes/life times you want (independent of each other even in the same runtime) without modififying the class.

 

Interesting I have always used Spring for dependency injection so interesting seeing a 'vanilla' approach particularly for situations where such libraries aren't available to be used. Wouldn't the second Java example result in a NullPointerException when run as the constructor isn't called since the instance isn't initialised?

 
 

The Java Singleton example could short as well:

public class ExampleTwo {

private static ExampleTwo instance = new ExampleTwo();

public static ExampleTwo getInstance() {
    return instance;
}

public void sayHello() {
    System.out.println("Hello!");
}

}

 

To add to Jordan's article, Dependency Injection (DI) helps you write unit testable code. Often an obstacle in unit testing is that the unit under test has two responsibilities intertwined: the dependencies it needs to carry out its business logic, and the business logic itself. The later is the thing you want to test. The former is a policy decision, which in DI could be deferred and swapped (by fakes or mocks for tests). However if you use statics (or import it, or instantiate it), this policy decision is baked into the implementation itself. This is a problem because now unit testing is not as straight forward: you need to setup the databases and services for each test, and for some external services, you don't control their states unless this is a feature they explicitly provide.

Obviously there are other ways of around this problem, depending on the stack you are working on, like monkey patching. But testability is designed into the code, and you need to think about this beforehand.

A side benefit of DI revolves around the swapping implementation part. It is a kind of code reusability you often didn't explicitly design for.

 
 

the idea of singleton does have its significance.

but testing static methods is not easy.

hence i prefer dependency injection over singleton - as test cases are of importance.

code of conduct - report abuse