trampster
trampster

Reputation: 8918

Does dependency injection break the Law of Demeter

I have been adding dependency injection to my code because it makes by code much easier to Unit test through mocking.

However I am requiring objects higher up my call chain to have knowledge of objects further down the call chain.

Does this break the Law of Demeter? If so does it matter?

for example: a class A has a dependency on an interface B, The implementation of this interface to use is injected into the constructor of class A. Anyone wanting to use class A must now also have a reference to an implementation of B. And can call its methods directly meaning and has knowledge of its sub components (interface B)

Wikipedia says about the law of Demeter: "The fundamental notion is that a given object should assume as little as possible about the structure or properties of anything else (including its subcomponents)."

Upvotes: 17

Views: 3160

Answers (8)

klenki
klenki

Reputation: 318

This also confused me for some time. In the wiki it also says...

An object A can request a service (call a method) of an object instance B, but object A should not "reach through" object B to access yet another object, C, to request its services. Doing so would mean that object A implicitly requires greater knowledge of object B's internal structure.

And this is the crux of the matter. When you interact with Class A you should not be able to interact with the state or methods of interface B. You simply shouldn't have access to its inner workings.

As for creating class A and knowing about interface B when creating objects; that's a different scenario altogether, it is not what the law of Demeter is trying to address in software design.

I would agree with other answers in that factories and a dependency injection framework would be best to handle this. Hope that clears it up for anyone else confused by this :)

Upvotes: 1

Suamere
Suamere

Reputation: 6258

Before I add my answer, I must qualify it. Service-Oriented Programming is built on top of OOP Principles and using OO Languages. Also, SOAs follow Inversion of Control and SOLID Principles to the teeth. So a lot of Service-Oriented programmers are surely arriving here. So, this answer is for Service-Oriented Programmers who arrive to this question, because SOA is built on top of OOP. This does no directly answer the OP's example, but does answer the question from an SOA Perspective.

In General, the Law of Demeter doesn't apply to Service-Oriented Architectures. For OO, the Law of Demeter is talking about "Rich Objects" in OOP which have properties and methods, and whose properties may also have methods. With OOP Rich Models, it is possible to reach through a chain of objects and access methods, properties, methods of properties, methods of properties' properties, etc. But in Service-Oriented Programming, Data (Properties) are separated from Process (Methods). Your Models (mainly) only have properties (Certainly never dependencies), and your Services only have Methods and dependencies on other Services.

In SOP, you can feel free to review the properties of a model, and properties of its properties. You won't ever be able to access methods you shouldn't, only a tree of data. But what about the Services? Does the Law of Demeter apply there?

Yes, the Law of Demeter Can Be applied to SOP Services. But again, the law was originally designed for Rich Models in OOP. And though the law Can Be applied to Services, proper Dependency Injection automagically fulfills the Law of Demeter. In that sense, DI Could not possibly break the law.

In limited opposition to Mark Roddy, I can't find any situation where you can legitimately talk about Dependency Injection and "consumers" in the same sentence. If by "consumers" you mean a class that is consuming another class, that doesn't make sense. With DI, you would have a Composition Root composing your object graph, and one class should never know another class even exists. If by "consumers" you mean a programmer, then how would they not be forced to "do the injection." The programmer is the one who has to create the Composition Root, so they must do the injection. A Programmer should never "do the injection" as an instantiation within a class to consume another class.

Please review the following example which shows actual separate solutions, their references, and the implementing code:

In SOA, DI doesn't allow breaking of LoD

In the top-right, we have the "Core." A lot of packages on NuGet and NPM have a "Core" Project which has Model, Interfaces, and possibly even default implementations. The Core should never ever ever depend on anything external.

In the top-left, we have an external implementation of the Core. The implementation depends on the Core, and so has knowledge of it.

In the bottom-left, we have a standalone Domain. The Domain has a Dependency on some Implementation of the Core, but Does not need to know about the implementation.

This is where I point out that neither the Domain nor the Implementation know each other exist. There is a 0% chance that either could ever reach into (Or beyond) the other one, because they don't even know they exist. The domain only knows that there is a contract, and it can somehow consume the methods by whatever is injected into it.

In the bottom-left is the Composition Root or Entry-Point. This is also known as the "Front Boundary" of the application. The root of an application knows all of its components and does little more than take input, determine who to call, compose objects, and return outputs. In other words, it can only tell the Domain "Here, use this to fulfill your contract for ICalculateThings, then give me the result of CalculateTwoThings.

There is indeed a way to smash everything into the same project, do concrete instantiations of Services, make your dependencies public properties instead of private fields, STILL Do Dependency-Injection (horribly), and then have services call into dependencies of dependencies. But that would be bad, m'kay. You'd have to be trying to be bad to do that.

Side-note, I over-complicated this on purpose. These projects could exist in one solution (as long as the Architect controls the Reference Architecture), and there could be a few more simplifications. But the separation in the image really shows how little knowledge the system has to have about its parts. Only the Composition Root (Entry Point, Front-Boundary) need to know about the parts.

Conclusion (TL;DR;): In Oldskewl OOP, Models are Rich, and the Law of Demeter can easily be broken by looking into models of models to access their methods. But in Newskewl SOP (built on top of OOP Principles and Languages), Data is separated from Process. So you can feel free to look into properties of models. Then, for Services, dependencies are always private, and nothing knows that anything else exists other than what they are told by abstractions, contracts, interfaces.

Upvotes: 0

Benk
Benk

Reputation:

Depends :-)

I think the top answer is not correct , even with a framework a lot of code uses Dependency injection and injects high level objects. You then get spaghetti code with lots of dependencies.

Dependency injection is best used for all the stuff that would pollute your object model eg an ILogger. If you do inject business object ensure its at the lowest level possible and try to pass it the traditional method if you can . Only use the dependecy injection if it gets to messy .

Upvotes: 0

Franci Penov
Franci Penov

Reputation: 76001

The Law of Demeter specifies that the method M of the object O can call methods on objects created/instantiated inside M. However, there's nothing that specifies how these objects were created. I think it's perfectly fine to use an intermediary object to create these, as long as that object's purpose in life is only that - creating other objects on your behalf. In this sense, DI does not break the Law of Demeter.

Upvotes: 1

aku
aku

Reputation: 124014

How does it break it? DI perfectly fits in idea of least knowledge. DI gives you low coupling - objects are less defendant on each other.

Citing Wikipedia:

...an object A can request a service (call a method) of an object instance B, but object A cannot “reach through” object B to access yet another object...

Usually DI works exactly the same way, i.e. you use services provided by injected components. If your object try to access some of the B's dependencies i.e. it knows much about B - that's leads to high coupling and breaks idea of DI

However I am requiring objects higher up my call chain to have knowledge of objects further down the call chain

Some example?

Upvotes: 5

Mark Roddy
Mark Roddy

Reputation: 27986

Dependency Injection CAN break the Law of Demeter. If you force consumers to do the injection of the dependencies. This can be avoided through static factory methods, and DI frameworks.

You can have both by designing your objects in such a way that they require the dependencies be passed in, and at the same time having a mechanism for using them without explicit performing the injection (factory functions and DI frameworks).

Upvotes: 14

David M. Karr
David M. Karr

Reputation: 15235

If I understand you correctly, this isn't caused by the use of dependency injection, it's caused by using mocking strategies that have you specify the function calls you expect a method to make. That's perfectly acceptable in many situations, but obviously that means you have to know something about the method you're calling, if you've specified what you think it's supposed to do.

Writing good software requires balancing tradeoffs. As the implementation becomes more complete, it becomes more inconsistent. You have to decide what risks those inconsistencies create, and whether they're worth the value created by their presence.

Upvotes: 2

John Mulder
John Mulder

Reputation: 10095

Does it break the law?
Strictly speaking, I think it does.
Does it matter?
The main danger of breaking the law is that you make your code more brittle.
If you really keep it to just the tests, it seems like that danger is not too bad.
Mitigation
My understanding of the Law of Demeter is that it can be followed by having "wrapper methods" which prevent directly calling down into objects.

Upvotes: 1

Related Questions