20

There are plenty of reasons why globals are evil in OOP.

If the number or size of the objects needing sharing is too large to be efficiently passed around in function parameters, usually everyone recommends Dependency Injection instead of a global object.

However, in the case where almost everyone needs to know about a certain data structure, why is Dependency Injection any better than a global object?

Example (a simplified one, to show the point generally, without delving too deep in a specific application)

There is a number of virtual vehicles which have a huge number of properties and states, from type, name, color, to speed, position, etc. A number of users can remote control them, and a huge number of events (both user-initiated and automatic) can change a lot of their states or properties.

The naive solution would be to just make a global container of them, like

vector<Vehicle> vehicles;

which can be accessed from anywhere.

The more OOP-friendly solution would be to have the container be member of the class which handles the main event loop, and be instantiated in its constructor. Every class which needs it, and is member of the main thread, will be given access to the container via a pointer in their constructor. For example, if an external message comes in via a network connection, a class (one for each connection) handling the parsing will take over, and the parser will have access to the container via a pointer or reference. Now if the parsed message results in either a change in an element of the container, or requires some data out of it to perform an action, it can be handled without the need of tossing around thousands of variables through signals and slots (or worse, storing them in the parser to be later retrieved by the one who called the parser). Of course, all classes which receive access to the container via dependency injection, are part of the same thread. Different threads will not directly access it, but do their job and then send signals to the main thread, and the slots in the main thread will update the container.

However, if the majority of classes will get access to the container, what makes it really different from a global? If so many classes need the data in the container, isn't the "dependency injection way" just a disguised global?

One answer would be thread safety: even though I take care not to abuse the global container, maybe another developer in the future, under the pressure of a close deadline, will nevertheless use the global container in a different thread, without taking care of all the collision cases. However, even in the case of dependency injection, one could give a pointer to someone running in another thread, leading to the same problems.

  • 6
    Wait, you talk about non-mutable globals and use a link to "why is global state bad" to justify it? WTF? – Telastyn Sep 22 '15 at 13:25
  • 1
    I'm not justifying globals at all. I think it's clear that I agree with the fact that globals are not the good solution. I'm just talking about a situation when even using dependency injection can be not much better than (or maybe even almost indistinguishable from) globals. So an answer might point out other hidden benefits of still using dependency injection in this situation, or that other approaches would be better than d.i. – vsz Sep 22 '15 at 13:29
  • 9
    But there is a decided difference between read only globals and global state. – Telastyn Sep 22 '15 at 13:31
  • 1
    possible duplicate of Should I use Dependency Injection or static factories? – gnat Sep 22 '15 at 13:41
  • 1
    @vsz not really - the question is about service locator factories as a way around the problem of many distinct objects; but its answers also answer your question of allowing the classes access to the global data rather than the global data being passed to the classes. – gbjbaanb Sep 22 '15 at 14:52
37

in the case where almost everyone needs to know about a certain data structure, why is Dependency Injection any better than a global object?

Dependency injection is the best thing since sliced bread, while global objects have been known for decades to be the source of all evil, so this is a rather interesting question.

The point of dependency injection is not simply to ensure that every actor who needs some resource can have it, because obviously, if you make all resources global, then every actor will have access to every resource, problem solved, right?

The point of dependency injection is:

  1. To allow actors to access resources on a need basis, and
  2. To have control over which instance of a resource is accessed by any given actor.

The fact that in your particular configuration all actors happen to need access to the same resource instance is irrelevant. Trust me, you will one day have the need to reconfigure things so that actors will have access to different instances of the resource, and then you will realize that you have painted yourself in a corner. Some answers have already pointed such a configuration: testing.

Another example: suppose you split your application into client-server. All actors on the client use the same set of central resources on the client, and all actors on the server use the same set of central resources on the server. Now suppose, one day, that you decide to create a "standalone" version of your client-server application, where both the client and the server are packaged in a single executable and running in the same virtual machine. (Or runtime environment, depending on your language of choice.)

If you use dependency injection, you can easily make sure that all the client actors are given the client resource instances to work with, while all the server actors receive the server resource instances.

If you do not use dependency injection, you are completely out of luck, as only one global instance of each resource can exist in one virtual machine.

Then, you have to consider: do all actors really need access to that resource? really?

It is possible that you have made the mistake of turning that resource into a god object, (so, of course everyone needs access to it,) or perhaps you are grossly overestimating the number of actors in your project that actually need access to that resource.

With globals, every single line of source code in your entire application has access to every single global resource. With dependency injection, each resource instance is only visible to those actors that actually need it. If the two are the same, (the actors that need a particular resource comprise 100% of the lines of source code in your project,) then you must have made a mistake in your design. So, either

  • Refactor that great big huge god resource into smaller sub-resources, so different actors need access to different pieces of it, but rarely an actor needs all of its pieces, or

  • Refactor your actors to in turn accept as parameters only the subsets of the problem that they need to work on, so they do not have to be consulting some great big huge central resource all the time.

  • I'm not sure I understand this - on one hand you say DI allows you to use multiple instances of a resource and globals do not, which is plainly nonsense unless you're conflating a single static variable against multiply instantiated objects - there's no reason that "the global" must be either a single instance or a single class. – gbjbaanb Sep 22 '15 at 14:30
  • 3
    @gbjbaanb Suppose that the resource is a Zork. The OP is saying that not only every single object in his system needs a Zork to work with, but also, that only one Zork will ever exist, and that all of his objects will only need access to that one instance of Zork. I am saying that neither of these two assumptions is reasonable: it is probably not true that all of his objects need a Zork, and there will be times when some of the objects will need a certain instance of Zork, while other objects will need a different instance of Zork. – Mike Nakis Sep 22 '15 at 14:40
  • 2
    @gbjbaanb I do not suppose you are asking me to explain why it would be stupid to have two global instances of Zork, name them ZorkA and ZorkB, and hard-code into the objects which one will use ZorkA and which one will use ZorkB, right? – Mike Nakis Sep 22 '15 at 14:41
  • 1
    I didn't say everyone, I said almost everyone. The point of the question was whether DI has other benefits besides denying access from those few actors who don't need it. I know that "god objects" are an anti-pattern, but blindly following best practices can also be one. It can happen that the entire purpose of a program is to do various things with a certain resource, in that case almost everyone needs access to that resource. A good example would be a program which works on an image: almost everything in it has something to do with the image data. – vsz Sep 23 '15 at 4:29
  • 1
    @gbjbaanb I believe the idea is to have one client class able to work exclusively on either ZorkA or ZorkB as pointed, not to have client class decide which of them to grab. – Eugene Ryabtsev Sep 23 '15 at 7:04
39

There are plenty of reasons why non-mutable globals are evil in OOP.

That is a dubious claim. The link you use as evidence refers to state - mutable globals. They are decidedly evil. Read only globals are just constants. Constants are relatively sane. I mean, you're not going to inject the value of pi into all your classes, will you?

If the number or size of the objects needing sharing is too large to be efficiently passed around in function parameters, usually everyone recommends Dependency Injection instead of a global object.

No, they don't.

If your functions/classes have too many dependencies, people recommend stopping and taking a look at why your design is so bad. Having a boatload of dependencies is a sign that your class/function is probably doing too many things and/or you do not have sufficient abstraction.

There is a number of virtual vehicles which have a huge number of properties and states, from type, name, color, to speed, position, etc. A number of users can remote control them, and a huge number of events (both user-initiated and automatic) can change a lot of their states or properties.

This is a design nightmare. You have a bunch of stuff that can be used/abused with no manner of validation, no manner of concurrency limitations - it's just coupling all over.

However, if the majority of classes will get access to the container, what makes it really different from a global? If so many classes need the data in the container, isn't the "dependency injection way" just a disguised global?

Yes, having coupling to common data everywhere isn't too different from a global, and as such, still bad.

The upside is that you're decoupling the consumers from the global variable itself. This provides you with some flexibility in how the instance is created. It also doesn't force you into having only one instance. You could make different ones to be passed along into your different consumers. You can also then create different implementations of your interface per consumer should you need to.

And due to change that inevitably comes with the shifting requirements of software, such a basic level of flexibility is vital.

  • sorry for the confusion with "non-mutable", I wanted to say mutable, and somehow didn't recognize my mistake. – vsz Sep 22 '15 at 19:50
  • "This is a design nightmare" - I know it is. But if the requirements are that all those events must be able to perform the changes in the data, then the events will be coupled to the data even if I split and hide them under a layer of abstractions. The whole program is about this data. For example, if a program has to perform a lot of different operations on an image, than all or almost all of its classes will somehow be coupled to the image data. Just saying "let's write a program which does something different" is not acceptable. – vsz Sep 22 '15 at 19:57
  • 2
    Applications that have many objects which routinely access some database are not exactly unprecedented. – Robert Harvey Sep 22 '15 at 19:59
  • @RobertHarvey - sure, but is the interface they depend on "can access a database" or "some persistent data store for foos"? – Telastyn Sep 22 '15 at 20:58
  • 1
    Reminds me of a Dilbert where PHB asks for 500 features and Dilbert says no. So PHB asks for 1 feature and Dilbert says sure. Next day Dilbert gets 500 requests each for 1 feature. If something doesn't scale, it just doesn't scale no matter how you dress it up. – corsiKa Sep 22 '15 at 22:12
16

There are three main reasons you should consider.

  1. Readability. If every unit of code has everything it needs to work on either injected or passed in as a parameter, it's easy to look at the code and instantly see what it's doing. This gives you a locality of function, which also enables you to separate concerns better and also forces you to think about...
  2. Modularity. Why should the parser know about the entire list of vehicles and all their properties? It probably doesn't. Maybe it needs to query whether a vehicle id exists or whether vehicle X has property Y. Great, that means you can write a service that does that, and inject only that service into your parser. Suddenly you end up with a design that makes far more sense, with every bit of code only handling data that is relevant to it. Which leads us to...
  3. Testability. For a typical unit test you want to set up your environment for many different scenarios and injection makes this very easy to implement. Again, returning to the parser example, do you really want to always hand-craft an entire list of fully-fledged vehicles for every test case you write for your parser? Or would you just create a mock implementation of the aforementioned service object, returning varying numbers of ids? I know which one I'd choose.

So to sum it up: DI isn't a goal, it's just a tool that makes it possible to achieve a goal. If you misuse it (like it appears you do in your example), you won't get any closer to the goal, there is no magical property of DI that will make a bad design good.

  • +1 for a concise answer focused on maintenance issues. More P:SE answers should be like this one. – dodgethesteamroller Sep 22 '15 at 22:16
  • 1
    Your sum up is so good. Soooo good. If you think [design pattern of the month] or [buzzword of the month] will magically solve your problems you're gonna have a bad time. – corsiKa Sep 23 '15 at 17:41
  • @corsiKa I was averse to DI (or Spring, to be more precise) for a long time because I couldn't see the point of it. It just seemed like an awful lot of hassle with no tangible gain (this was back in the XML configuration days). I wish I had found some documentation on what DI actually buys you back then. But I didn't, so I had to figure it out myself. It took a long time. :) – biziclop Sep 24 '15 at 10:38
3

Its really about testability - normally a global object is very maintainable and easy to read/understand (once you know its there, of course) but it is a permanent fixture and when you come to split your classes into isolated tests, you find that your global object is stuck saying "what about me" and thus your isolated tests require the global object to be included in them. It doesn't help that most test frameworks don't easily support this scenario.

So yes, its is still a global. The fundamental reason for having it has not changed, only the manner in which it is accessed.

As for initialisation, this is still a problem - you still have to set the configuration so your tests can run, so you're not gaining anything there. I suppose you could split a large dependant object into many smaller ones and pass the appropriate one to the classes that need them, but you're probably getting even more complexity to outweigh any benefits.

Regardless of all that, its an example of the 'tail wagging the dog', the need to test is driving how the codebase is architected (similar problems arise with items such as static classes, eg current datetime).

Personally I prefer to stick all my global data in a global object (or several) but provide accessors to get the bits my classes need. Then I can mock those to return the data I want when it comes to testing without the added complexity of DI.

  • "normally a global object is very maintainable" - not if it is mutable. In my experience, having so much mutable global state can be a nightmare (though there are ways to make it bearable). – sleske Sep 23 '15 at 7:06
3

In addition to answers already u have,

There is a number of virtual vehicles which have a huge number of properties and states, from type, name, color, to speed, position, etc. A number of users can remote control them, and a huge number of events (both user-initiated and automatic) can change a lot of their states or properties.

One of characteristic of OOP programming is to only keep properties you really need for a specific object,

NOW IF your object has too many properties - you should break your object down further into sub-objects.

Edit

Already mentioned why global objects are bad, I would add one example to it.

You need to do unit testing on a windows form's GUI code, if you use global, you will need to create all of button and it's events for example to create a proper test case...

  • True, but for example, the parser will be required to know everything because of any command might be received which can make changes in anything. – vsz Sep 22 '15 at 13:34
  • 1
    "Before I come to your actual question"... are you going to answer his question? – gbjbaanb Sep 22 '15 at 13:35
  • @gbjbaanb question has got a lot of text, please allow me some time to read it, properly – Pointless-Ai Sep 22 '15 at 13:37

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.