Dependency Injection & Singleton Design pattern
Asked Answered
I

7

108

How do we identify when to use dependency injection or singleton pattern. I have read in lot of websites where they say "Use Dependency injection over singleton pattern". But I am not sure if I totally agree with them. For my small or medium scale projects I definitely see the use of singleton pattern straightforward.

For example Logger. I could use Logger.GetInstance().Log(...) But, instead of this, why do I need to inject every class I create, with the logger's instance?.

Inflexible answered 18/4, 2010 at 15:55 Comment(0)
P
73

If you want to verify what gets logged in a test, you need dependency injection. Furthermore, a logger is rarely a singleton - generally you have a logger per each of your class.

Watch this presentation on Object-oriented design for testability and you'll see why singletons are bad.

The problem with singletons is that they represent a global state which is hard to predict, especially in tests.

Have in mind that an object can be de-facto singleton but still be obtained via dependency-injection, rather than via Singleton.getInstance().

I'm just listing some important points made by Misko Hevery in his presentation. After watching it you will gain full perspective on why it is better to have an object define what its dependencies are, but not define a way how to create them.

Purgation answered 18/4, 2010 at 16:0 Comment(1)
I have a doubt, What about IoC containers like Spring or NestJs, by default they create singleton instances, what's wrong with that compared to a singleton thats implements an interface and then is injected by constructor for example (suppose we don't use any framework).Bereave
H
116

Singletons are like communism: they both sound great on paper, but explode with problems in practice.

The singleton pattern places a disproportionate emphasis on the ease of accessing objects. It completely eschews context by requiring that every consumer use an AppDomain-scoped object, leaving no options for varying implementations. It embeds infrastructure knowledge in your classes (the call to GetInstance()) while adding exactly zero expressive power. It actually lessens your expressive power, because you can't change the implementation used by one class without changing it for all of them. You simply can't add one-off pieces of functionality.

Also, when class Foo depends on Logger.GetInstance(), Foo is effectively hiding its dependencies from consumers. This means that you can't fully understand Foo or use it with confidence unless you read its source and uncover the fact that it depends on Logger. If you don't have the source, that limits how well you can understand and effectively use the code on which you depend.

The singleton pattern, as implemented with static properties/methods, is little more than a hack around implementing an infrastructure. It limits you in myriad ways while offering no discernible benefit over the alternatives. You can use it however you'd like, but as there are viable alternatives which promote better design, it should never be a recommended practice.

Humism answered 18/4, 2010 at 16:41 Comment(11)
@BryanWatts all that said, Singletons are still much quicker and less error prone in a normal medium scale application. Usualy I don't have more than one possible implementation for a (custom) Logger so why the ** would I need to: 1. Create an interface for that and change it each time I need to add/change public members 2. Maintain a DI configuration 3. Hide the fact that there is a single object of this type in the entire system 4. Bind myself to a strict functionality caused by a premature Separation Of Concerns.Karinekariotta
@UriAbramson: It seems you've already made the decision about the tradeoffs you prefer, so I won't try to convince you.Humism
@BryanWatts Its not the case, perhaps my comment was a bit too harsh but it actualy does interest me a lot to hear what you have to say about the point I brought up...Karinekariotta
@UriAbramson: Fair enough. Would you at least agree that the swapping of implementations for isolation during testing is important?Humism
@BryanWatts Agree on that one. We are not doing automated unit tests here yet... It might become a problem when more devs join in (currently 3). Though migrating from singletons to DI shouldn't be too much of a problem.Karinekariotta
@UriAbramson: Then, you are asking about doing a one-off singleton in a context in which DI is already in effect?Humism
@BryanWatts No, what i'm saying (or asking...) is basicaly, if you don't have automatic unit tests, why would you need DI? doesn't it just make a simple task more complicated than it actually is? For instance, we have a desktop client application where a user can start a session and then end it after a few minutes. All classes in the application have access to UserSessionManager.Instance. So instead of keeping a class, an interface, a configuration, a ctor param & private member for each new class that needs to aceess UserSessionManager I can access the singleton from anywhere and it works okKarinekariotta
Let us continue this discussion in chat.Karinekariotta
The use of singletons and dependency injection is not mutually exclusive. A singleton can implement an interface, therefore can be used to satisfy a dependency on another class. The fact that it is a singleton does not force every consumer to obtain a reference through it's "GetInstance" method/property.Waac
@Oliver: Agreed - see "The singleton pattern, as implemented with static properties/methods..."Humism
@Uri 1. If there is only one implementation it doesn't need an interface yet 2. Testing a Singleton requires configuration 3. DI is explicit; nothing is hidden 4. DI is neither strict nor premature.Shaughn
P
73

If you want to verify what gets logged in a test, you need dependency injection. Furthermore, a logger is rarely a singleton - generally you have a logger per each of your class.

Watch this presentation on Object-oriented design for testability and you'll see why singletons are bad.

The problem with singletons is that they represent a global state which is hard to predict, especially in tests.

Have in mind that an object can be de-facto singleton but still be obtained via dependency-injection, rather than via Singleton.getInstance().

I'm just listing some important points made by Misko Hevery in his presentation. After watching it you will gain full perspective on why it is better to have an object define what its dependencies are, but not define a way how to create them.

Purgation answered 18/4, 2010 at 16:0 Comment(1)
I have a doubt, What about IoC containers like Spring or NestJs, by default they create singleton instances, what's wrong with that compared to a singleton thats implements an interface and then is injected by constructor for example (suppose we don't use any framework).Bereave
W
20

Others have explained very well the problem with singletons in general. I would just like to add a note about the specific case of Logger. I agree with you that it is usually not a problem to access a Logger (or the root logger, to be precise) as a singleton, via a static getInstance() or getRootLogger() method. (unless if you want to see what gets logged by the class you are testing - but in my experience I can hardly recall such cases where this was necessary. Then again, for others this might be a more pressing concern).

IMO usually a singleton logger is not a worry, since it does not contain any state relevant to the class you are testing. That is, the logger's state (and its possible changes) have no effect whatsoever on the state of the tested class. So it does not make your unit tests any more difficult.

The alternative would be to inject the logger via the constructor, to (almost) every single class in your app. For consistency of interfaces, it should be injected even if the class in question does not log anything at present - the alternative would be that when you discover at some point that now you need to log something from this class, you need a logger, thus you need to add a constructor parameter for DI, breaking all client code. I dislike both of these options, and I feel that using DI for logging would be just complicating my life in order to comply with a theoretical rule, without any concrete benefit.

So my bottom line is: a class which is used (almost) universally, but does not contain state relevant to your app, can safely be implemented as Singleton.

Wiersma answered 18/4, 2010 at 22:12 Comment(6)
Even then, a singleton can be a pain. If you realize your logger needs some additional parameter for some cases, you either get to make a new method (and let the logger get uglier), or break all consumers of the logger, even if they don't care.Cristophercristy
@kyoryu, I am talking about the "usual" case, which IMHO implies using a (de facto) standard logging framework. (Which typically are configurable via property / XML files btw.) Of course there are exceptions - as always. If I know my app is exceptional in this respect, I won't use a Singleton indeed. But overengineering saying "this might be useful sometime" is almost always a wasted effort.Volnak
If you're already using DI, it's not much extra engineering. (BTW, I don't disagree with you, I'm the upvote). A lot of loggers require some "category" information or something of the sort, and adding additional parameters can cause pain. Hiding it behind an interface can help keep the consuming code clean, and can ease switching to a different logging framework.Cristophercristy
@kyoryu, sorry for the wrong assumption. I saw a downvote and a comment, so I connected the dots - the wrong way, as it turns out :-( I have never experienced the need to switch to a different logging framework, but then again, I understand it may be a legit concern in some projects.Volnak
Correct me if I am wrong, but the singleton would be for the LogFactory, not the logger. Plus the LogFactory is likely to be the apache commons or Slf4j logging facade. So switching logging implementations is painless anyway. Isn't the real pain with using DI to inject a LogFactory the fact that you have to go to the applicationContext now to make every instance in your app?Dm
thus you need to add a constructor parameter for DI, breaking all client code. This is only true without a DI container. If you work with DI using a container (which I can only assume is the prevalent use-case) then consumers of your changed class aren't affected because they don't run the class' constructor - the DI container does. So while I agree with your statement can safely be implemented as Singleton - I don't see the added value over DI (with container).Panay
D
11

It's mostly, but not entirly about tests. Singltons were popular because it was easy to consume them, but there are a number of downsides to singletons.

  • Hard to test. Meaning how do I make sure the logger does right thing.
  • Hard to test with. Meaning if I'm testing code that uses the logger, but it's not the focus of my test, I still need to ensure my test env supports the logger
  • Sometimes you don't want a singlton, but more flexibilty

DI gives you the easy consumption of your dependent classes - just put it in the constructor args, and the system provides it for you - while giving you the testing and construction flexibility.

Dispel answered 18/4, 2010 at 16:10 Comment(1)
Not really. It's about shared mutable state and static dependencies making things painful in the long run. Testing is just the obvious, and frequently the most painful, example.Cristophercristy
C
5

About the only time you should ever use a Singleton instead of Dependency Injection is if the Singleton represents an immutable value, such as List.Empty or the like (assuming immutable lists).

The gut check for a Singleton should be "would I be okay if this was a global variable instead of a Singleton?" If not, you're using the Singleton pattern to paper over a global variable, and should consider a different approach.

Cristophercristy answered 18/4, 2010 at 22:19 Comment(0)
J
1

Just checked out the Monostate article - it's a nifty alternative to Singleton, but it has some wierd properties:

class Mono{
    public static $db;
    public function setDb($db){
       self::$db = $db;
    }

}

class Mapper extends Mono{
    //mapping procedure
    return $Entity;

    public function save($Entity);//requires database connection to be set
}

class Entity{
public function save(){
    $Mapper = new Mapper();
    $Mapper->save($this);//has same static reference to database class     
}

$Mapper = new Mapper();
$Mapper->setDb($db);

$User = $Mapper->find(1);
$User->save();

Isn't this kind of scary - because the Mapper is really dependent on the database connection to perform save() - but if another mapper has been created prior - it can skip this step in acquiring it's dependencies. While neat, it's also kind of messy isn't it?

Jeffjeffcoat answered 24/4, 2010 at 13:19 Comment(0)
F
0

There are other alternatives to Singleton: Proxy and MonoState patterns.

http://www.objectmentor.com/resources/articles/SingletonAndMonostate.pdf

How can the proxy pattern be used to replace a singleton?

Fabrication answered 21/4, 2010 at 12:29 Comment(1)
The first link doesn't work. Is this the original target? staff.cs.utu.fi/~jounsmed/doos_06/material/…Ludvig

© 2022 - 2024 — McMap. All rights reserved.