6

I'm working with a project that our architect has decided to use dependency injection for almost everything. We use an IoC container. One of the main issues that I keep coming across when using this pattern is "How do I pass this bit of data here to a different object that will be used later?"

Often, the easiest solution is to mark a specific class as being a "singleton" with our specific injector/container. This is bad for a few reasons. As we all know, singletons are evil and should never be used. Sometimes the class marked as "singleton", should actually have multiple instances when used in different parts of the code. Marking a class as a "singleton" creates a new dependency that we use an injector/container that supports the concept of marking a class as a "singleton".

Another solution is the modify how the injector/container creates the object by writing closures, or other specific logic when the injector is instantiating the object based on what is client is using it and the state of the clients. This is bad because it creates a new dependency of this functionality existing in the injector/container. It also violates the separation of concerns because it now makes the inject/container concerned with several different classes.

Here is an example use case of this problem:

  1. I am using ClassA early in the application from ClassB. I have some state/data I want to store in ClassA and use it later.
  2. Some more stuff happens in the application. The object of ClassA is either still on the stack farther down, or that part of the stack no longer exists and maybe there are no references to the object of ClassA. The method of classB may or may not still be on the call stack.
  3. Later in the application, I want to refer to the state/data of ClassA from ClassC.

Update - I'm adding an example

class ClassA {
private:
    shared_ptr<ClassState> state;
    shared_ptr<ClassB> b;
public:
    ClassA(shared_ptr<ClassState> state, shared_ptr<ClassB> b) {
        this->state = state;
        this->b = b;
    }
    void run() {
        // do stuff
        if (true /* blah blah blah */) {
            state->setState(true);
        } else {
            state->setState(false);
        }
        // do more stuff
        b->run();
    }
};

class ClassB {
private:
    shared_ptr<ClassC> c;
public:
    ClassB(shared_ptr<ClassC> c) {
        this->c = c;
    }
    void run() {
        // do stuff
        c->run();
        // do more stuff
    }
};


class ClassC {
private:
    shared_ptr<ClassState> state;
public:
    ClassC(shared_ptr<ClassState> state) {
        this->state = state;
    }
    void run() {
        //This is where I want to use that data that was stored earlier
        // in the application
        if (state->getState()) {
            // do stuff
        }
    }
};

void main() {
    // Create whatever magicaly IOC dependency injector
    shared_ptr<ClassA> a = injector.create<ClassA> ();
    a->run();
}
9
  • So you want to access the state of an object that does not exist any more? I do not think you can do that. Nov 1, 2017 at 15:54
  • I think in 1. you mean you are using an instance of ClassA and you want to store state in that instance. If you are storing state in the class, well don't do that but you should be able to reference ClassA whenever.
    – JimmyJames
    Nov 1, 2017 at 16:44
  • @Goyo, no, I do want the object to exist. I just don't know a way of passing the object from one place to another. Nov 1, 2017 at 20:22
  • 1
    @JacobBrown Then what does "that part of the stack no longer exists and maybe there are no references to the object of ClassA" mean? Either the object does not exist or there is a memory leak. Nov 1, 2017 at 20:39
  • 1
    @Jesper It's not a singleton if it's stateless. That defeats the entire purpose of a singleton. Unless your program is just one giant function that takes no user input while it's running, you need state. In OO land, singleton services work quite well to manage state. Nov 2, 2017 at 15:56

4 Answers 4

6

It sounds like you're wanting to just reach out and grab things as soon you need them. That's typical of procedural programming. No language, framework, pattern, or tool can stop you if you insist on working this way.

However, there is another way. Tell, don't ask says not to reach out for what you need (as is typically done through getters and static references) but to just let what ever you need be handed to you. That way you don't have to know how to find it. It finds you.

So you have ClassC that needs something from ClassA. Rather than write ClassB.ClassA.getNeededThing() write

class ClassC {
    String neededThing;
    public ClassC(String neededThing) { 
        this.neededThing = neededThing;
    }         
} 

This way ClassC doesn't even exist until it has what it needs to be useful. And it doesn't care if it was ClassA or ClassB that figured out how to get it what it needs.

Now that's just object creation. After objects are created they can still talk to each other if they have references to each other. Say there was anotherNeededThing that ClassC needs from ClassA.

class ClassA {
    ClassC c;
    String someOtherNeededThing;
    public ClassA(ClassC c, String someOtherNeededThing){ 
        this.c = c;
        this.someOtherNeededThing = someOtherNeededThing;
    }
    public void timeToGiveTheThing() {
        c.doYourThingWith(someOtherNeededThing);
    }      
} 

Done this way ClassC doesn't even need to know ClassA exists.

You might think you need a fancy DI container to make all this work but it can be done in main (if you have one, if not find your "composition root" and do it there).

int main() {
    ClassC c = new ClassC("Hello ");
    ClassA a = new ClassA("world", c);
    a.timeToGiveTheThing();
}

But you're stuck with a DI container you say? Fine, just list C as one of A's dependencies. So long as you get a reference to it you can talk to it. So long as whatever knows when this should happen has a reference to you then you will talk to it when you should. No need to ask.

If you come from procedural programming This will feel very different from your usual way of programming. It is a different way. We call this style object oriented programming. If you thought that you were doing that before because you were using an OOP language I'm sorry, that doesn't guarantee you're using OOP.

Once you get your head around this you'll likely find yourself creating constructors and method signatures with tons of arguments. That's when it's time to learn about primitive obsession and parameter objects. Just don't confuse parameter objects with encapsulated objects.

13
  • So basically what you're saying is that the DI container merely fulfills each class's dependencies, and it is up to the programmer to implement communication in the usual mundane ways like composition? Nov 1, 2017 at 15:14
  • 5
    @RobertHarvey There certainly are DI libraries/frameworks that hype other reflection-esq features but mostly this is it. They might also tempt you to move your composition into another language like xml or pollute you with proprietary @ thingies but yeah. Dependency Injection is just good old fashioned reference passing with a fancy name. Nov 1, 2017 at 15:19
  • 5
    Half the reason my answer is hard to understand is because these names give no hint to their purpose. I'm not "outside the dependency injection". Objects that want to talk to other objects need to be introduced to them. We do that by passing references around. Dependency Injection is just a fancy word for it. We're trying to build an object graph. No cycles required. If you can't do it in main find the highest place in the call stack that you can. That's your composition root. When done correctly main is many lines of construction followed only one call that starts the whole graph ticking. Nov 1, 2017 at 20:16
  • 1
    @JacobBrown: en.wikipedia.org/wiki/Mediator_pattern Nov 1, 2017 at 20:47
  • 1
    No, I'm saying the polite way to share data is by first being introduced so that you don't throw a tantrum when your best friend is replaced. The rude way is to make anything you want static and global and grab it whenever you like. That works. But it's not easy to maintain. Main is the ideal place to be introduced. Those you meet here will live as long as you do. If separating construction code and behavior code can be achieved at this level then you don't have to hunt for where something is created. If you can't do that there then do it as high up the call stack as you can. Nov 1, 2017 at 21:51
1

2.Some more stuff happens in the application. The object of ClassA is either still on the stack farther down, or that part of the stack no longer exists and maybe there are no references to the object of ClassA. The method of classB may or may not still be on the call stack.

If this is the case, then before Class A disappears, it needs to persist it's data to store of some sort so class C can access that data.

If you are using dependency injection to inject an instance of Class A into Class C because you need it's data, you will have to choose what type of object lifetime class A will need; per instance, single, etc.

It sounds like the DI container may need to create a single instance of that class if you need to need to keep that class alive. DI containers have options for management of object lifetime, or just persist the data and have Class C get the data and remove the dependency from Class A.

1

I am having a hard time, understanding your problem, especially understanding in which way you see your problem related to dependency injection (DI). Your problem looks from a 30000ft view more like an architectural / organizational problem within your codebase, than a problem of DI. Without DI your objects have to solve this problem anyways.

There are several constructs to help your objects communicate to each other.

The simplest one is using the observer pattern. Where one object is the source (Subject) of information and one or more objects subscribe (Observer) to messages emitted form the source. Everytime the source emits an event, the observers are notfied. This works for the lifetime of both: Subject and Observer. And since both are linked through reference, so is their lifetime.

If want to decouple more you have to use some external source of truth:

e.g.

  • Information is persisted in form of a file in the filesystem
  • Information is persisted as data in the database
  • Information is persisted to a running process (in memory database, Genserver, Message Queue, etc.)

Your Subject becomes a Producer and the Observer becomes a Consumer and the communication becomes asynchronous.


"How do I pass this bit of data here to a different object that will be used later?"

Communicate the change synchronous via Observer Pattern or asynchronous.

Often, the easiest solution is to mark a specific class as being a "singleton" with our specific injector/container.

From what you write, I assume, you deal with DI in a web application context. There is no problem of using Singleton say in a Spring-Application. Singleton means in this context, that the object is created once and not for every request (cf. Quick Guide to Spring Bean Scopes). If you ensure race condition free access, there is nothing to say against.

But on the other hand prototype-Annotated beans live only for the time being consumed and are generated everytime they are asked for. These could be stateful, but their state ceases with their lifetime.

Another solution is the modify how the injector/container creates the object by writing closures, or other specific logic when the injector is instantiating the object based on what is client is using it and the state of the clients.

This is not really different from using a singleton.

Here is an example use case of this problem:

As said above, you have to establish communication between your objects.

3
  • I don't see how observer pattern could help me. If I could connect one object to another (and have one be observer and other be subject), then wouldn't I also be in position to just simple pass the data from one to the other? I guess you could say that the data isn't ready at that point, but if I passed a reference to the object where the data would be stored, then it would be there. So I don't see how observer pattern could help. Nov 1, 2017 at 20:17
  • The other example you gave of passing data via files, databases, or in memory database, seems just as bad, or worse as setting the object to be a singleton in the injector/container. Both ways have the singleton problem, but using external ways of storing the data just causes more places for failure, more latency, and more things to manage. Nov 1, 2017 at 20:18
  • 1
    @JacobBrown These are the two ways to solve your problem: a) establish communication directly via sharing a reference somehow or b) establish communication indirectly via an "external" component. This may be a form of persistence or in form of a mediator. Nov 2, 2017 at 8:38
1

Singletons are discouraged when they are associated with a global static reference because they hide dependencies between classes. Singletons in the context of a DI container are actually a great idea.

  1. Create a singleton class responsible for managing that particular data in a thread safe way
  2. Create an abstract class to be the public interface for that singleton which provides read/write access to the data
  3. Use constructor injection to share the data between the objects that need it using the public interface.

Simple, safe, and easy to understand.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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