1

Starting to explore using DI more in my project design and I find myself asking the same question:

"If I pass this object to that constructor, does that new object now have a dependency?"

For example, if I had the following:

class App:

    ROOT_DIR = Path('some_dir')
    SOME_CFG = Path(ROOT_DIR, 'some_cfg.cfg')

    def __init__(self, config: Config) -> None:
        self.config = config

class Config:

    def __init__(self, file: Path) -> None:
        self.file = file

From that, App has a dependency on Config because Config will be responsible for all configuration tasks.

Where I start to question it is how Config is constructed: Config(App.SOME_CFG).

So if I pass anything to the constructor of Config, does that mean it has a dependency?

  • In the broad sense, everything your object uses is a dependency - specifically, it depends on some "contract" that it expects the other party to follow. E.g. your object might rely on another objects interface (methods and properties, supported operators, etc.), or on some convention (expected structure of data, special cases, meaning of special values), etc. But generally, we don't all care that much about trivial kinds of dependencies (e.g. on built in types); that's not what DI is about. – Filip Milovanović May 7 at 1:17
  • But note that classes can have dependencies that don't appear in the constructor - e.g. if you're accessing global objects. Even in your example, what Config really depends is the format of the config file, and that's not something that's in the constructor. Most of the time, this is not a problem, but if you wanted to break that dependency, you'd introduce some kind of an abstract "config reader" that you'd then pass to the constructor. Having an abstract dependency lets your class be independent from what's behind the abstraction - that's the point of DI. – Filip Milovanović May 7 at 1:17
  • @Alexander-ReinstateMonica thanks for the suggested edit, but it only works if you use lowercase, i.e. python. – Glorfindel May 7 at 6:36
5

If I pass this object to that constructor, does that new object now have a dependency?

Nope. Just 'cause you pass it, that doesn't guarantee it gets used. It might be fine to pass null.

To have a dependency means you depend on it. You need it.

Now it's true that, mechanically, Dependency Injection is just reference passing with a fancy name. But the idea behind it is to say that the things you depend on should be passed to you. You shouldn't build them yourself or figure out how to find them. That's the fundamental requirement of the DI 'pattern'.

This prevents you from hiding the dependency. You must explicitly ask for what you need. Code that does that is far easier to maintain because it doesn't hide its needs.

Now if you pervert that, and ask for things you don't need, well you're just being a jerk.

| improve this answer | |
1

Literally? Yes, along with the wires and doped silicon.

Conceptually? No.

Data Structure vs. Object

A Data Structure is something your code imposes on a region of memory. While a different region of memory can be passed in, the range of behaviours are not. These are supplied by the reader.

Native data types generally are Data Structures: ints, bools, pointers, even strings (in some languages). Also include basic structures such as tuples, arrays, stack frames, object storage, static storage, etc... There are of course exceptions.

An Object is completely isolated from your code. Object's provide an interface through which your code can communicate with them. That interface is usually pre-agreed data structures known as stack frames, though other techniques might be used, such as datagrams on a network.

By this definition functions are objects, so are instances of class types, or generally anything for which you can actually separate the interface from an implementation. This does mean some basic types are also objects in some languages, such as Java's integer which is both a data structure (unboxed) and an object (boxed).

Dependency Injection

Objects often require other objects as collaborators. Like good object users they do not care which specific object they collaborate with, simply that they provide a given interface.

Dependency Injection is a technique for provisioning an object with its collaborators.

The question to ask is this: Does my object copy or reference the argument it is passed?

  • If it copies, then the argument is being treated as a data-structure.
  • It it references, then the argument is being treated as an object.
| improve this answer | |
  • 1
    I (strongly) disagree. Parameter passing is both literally and conceptually DI. Everything else — the whole cottage industry that has developed around IoC containers and DI frameworks — is a completely unnecessary distraction, bordering on cargo cult. It can be convenient, but it’s not needed, and it doesn’t “define” DI. And your mentions of “memory regions” and “native types” is irrelevant in this context. – Konrad Rudolph May 7 at 11:20
  • @KonradRudolph I agree on the literal, yet cannot agree with you on the conceptual. A parameter may be passed in via the constructor that the object is not dependant on. Consider an object with a tagging construct. It does not depend on the tagging construct, even though it has room for it, and for convenience it is configurable from a constructor. Deleting this parameter will not affect the object in question, and ergo not a dependency even in the most trivially conceived of sense. I don't recall mentioning IoC containers or DI frameworks, was there a point in there? – Kain0_0 May 8 at 7:19
  • If anything, your counter-example shows that it’s not literally DI, even if it is conceptually. But of course all you’re showing is that not all parameter passing is DI, only one that establishes a dependency. “Duh”, if I may say so. — But apparently I don’t really understand what you’re claiming, and your mention of wires, doped silicon, memory regions and native types doesn’t help alleviate the confusion. – Konrad Rudolph May 8 at 13:34
  • @KonradRudolph ah, now I think i see the problem. My point was that an argument passed to a constructor is required, as in it is depended upon for construction. To that i point out that many things are pre-requisites (dependencies) to constructing the object including the machine itself, which isn't a very useful definition of dependency in the context of objects. I then point out that a value's behaviour is fully specified apriori, thus whichever state submitted cannot extend behaviour. Instead the interesting dependency is that of an interface which specifies communication but not behaviour. – Kain0_0 May 10 at 22:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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