Search code examples
oopsoftware-qualitylaw-of-demeter

Law of Demeter can easily be bypassed?


Is it always possible to work around the Law of Demeter simply by creating more methods?

Some people mention that this is not valid (http://wiki.c2.com/?LawOfDemeterIsHardToUnderstand), but it seems like it should be valid, since the Law of Demeter allows sending messages to any parameter.

For example, this violates the Law of Demeter (assuming m1 does not return a1) but can be refactored:

class C1 {
    m1 (a1: C2) {
        a1.m1().m2() // not allowed
    }
}

The access to m2 can be refactored into another method:

class C1 {
    m1 (a1: C2) {
        m2(a1.m1()) // allowed
    }

    m2 (a1: C3) {
        a1.m2()
    }
}

Solution

  • "When your method takes parameters, your method can call methods on those parameters directly."

    I'd say the way it's written leaves to interpretation whether the whole method's execution stack should be considered or not (to me it's implied it's to be considered). If it is then it's a violation no matter the added level of indirection (m2) and if it's not then the code doesn't violate the rule. However, this is a guideline and it won't help developers who don't understand the goal it serves: increase encapsulation and limit coupling.

    There's certainly a multitude of creative ways to create awful models that still complies to rules & definitions of most design principles, but that's obviously not the point. That's like stating private modifiers can easily be by-passed using reflection: that's true, but it's pretty hard to do that by mistake.

    In terms of encapsulation and coupling, there's no distinction between the code you posted and a1.m1().m2(): C1 is coupled to C2 AND C3 which was acquired through C2. The fact C2 clients (e.g. C1) need to dig it's internals is a good indicator that it's encapsulation could be improved.

    Another principle which goes hand in hand with the Law of Demeter is the Tell Don't Ask principle, which states that objects should be told what to do rather than asked for their state. This also favors better encapsulation and therefore reduces coupling.

    One important note to make though is that there are two types of objects, data structures and "real objects" (data & behaviors). Data structures (e.g. DTOs -- getter/setter bags) aren't subject to most design principles such as the Law of Demeter as their role is solely to represent and share data. However, such data structures are usually found at system boundaries (e.g. application services, HTTP APIs, etc.) and having many of such objects in a model's core is an object-oriented code smell.