Pure Code: The Obscure Law of Demeter

I am currently reading the book Pure code on a small chapter about the law of Demeter. Before that, I encountered it in the patterns from O'reilly (HeadFirst) and there was a little and clear. After a clean code , my head is in a mess. I have a couple of questions:

1) Martin divides objects into objects and data structures. What is meant by structure here? Struct (in c#, for example) or in general, an object of a class in which there is nothing else except open fields (or properties) (methods, for example)?

2) The first question implies a lack of understanding of what is meant by a hybrid (half object, half data structure), which is better not to write. Is it considered a hybrid, for example, an object of the "Car" class, where you can see its color, and even change the wheels, and it also has a behavior?

3) This question was raised for oral discussion in other developers (we are not juns, but we are not "coolies") and there was also a misunderstanding of the very essence of this law, why it is necessary. so, what is the main reason: either a base for further unbearable changes, or hiding the internal structure of the object, or even easily preventing and handling NullReferenceException? Everyone understands it differently.

4) Is the law violated when dividing the "bad" methods into many small ones?

It was

public class Class1
{
    public String DoSmth()
    {
        //Вроде как закон нарушен, потому что вызываем метод у вернувшегося значения
        return MethodClass1().DoSmth();
    }

    public Class2 MethodClass1()
    {
        return new Class2();
    }
}

public class Class2
{
    public String DoSmth()
    {
        String res2 = this.MethodClass2();
        return res2;
    }

    public String MethodClass2()
    {
        return "test";
    }
}

Became

public class Class1
{
    public String DoSmth()
    {
        //теперь тут вызываются только методы того же класса
        Class2 res1 = MethodClass1();
        return this.MethodClass1_2(res1);
    }

    public Class2 MethodClass1()
    {
        return new Class2();
    }

    public String MethodClass1_2(Class2 val)
    {
        return val.DoSmth();
    }
}

Another small update: personally, I have developed this attitude so far: this is some kind of rule, the need for a clear violation which is an indicator that something is not designed correctly and you can find a solution that will be better both in business logic and in further maintenance.

Author: user2216, 2016-05-17

3 answers

In the book" The Programmer-Pragmatist " (Hunt, Thomas), Demeter's law (such a crooked translation) is reduced to a short statement:

Minimize binding between modules

Example from the book in C++

class Demeter
{
    private:
        A* a;
        int func();
    public:
        void example(B& b);
}

void Demeter::example(B& b)
{
    int f = func(); // 1

    b.invert();     // 2

    a = new A();
    a->setActive(); // 3

    C c;
    c.print();      // 4
}

The Law of Demeter for functions states that any method of some object can only access methods belonging to:

  1. to ourselves
  2. any parameters passed to the{[13] method]}
  3. by any created by it objects
  4. any directly contained component objects

Based on this, we can answer your question # 3: reducing connectivity makes it easier to modify and maintain code, and makes it easier to write tests.

In practice, this means that you will have to create a large number of wrapper methods that simply forward the request further to the delegate. These wrapper methods incur costs, impairing performance and taking up memory.

Paying attention the law of Demeter and tightly linking several modules, you can get a performance gain.

 4
Author: Alexander Petrov, 2016-05-17 11:04:26

1) He explicitly writes about the structure there:

On the other hand, if ctxt, Options, and ScratchDir are ordinary data structures that do not have the behavior of , then they naturally reveal their internal structure, and the law of Demeter does not apply to them.

The behavior is not hidden in any way if the calling code accesses the data directly, and not through get/set (this is a simple structure, as in C, for example). At the same time, if the conversion goes through get/set does not mean that some behavior is hidden there (it may be hidden there, but not the fact), and therefore:

The use of access functions complicates the situation.

2) And about hybrids, it seems to be available:

Hybrids contain both functions for performing important operations, as well as open variables or open read/write methods that can be used to perform important operations. in all respects, make private variables public.

To my mind, hiding the behavior of and is a key factor. If there is some behavior and it is hidden, then it is an object, if there is no behavior, but just open data, then it is a structure, and if there is both hidden behavior and open data, then this is a hybrid.

 2
Author: zed, 2016-05-17 08:32:25

I will answer the 4th question. If "Was" and "Became" are left as they are, then the law is violated in both cases. What are the "violations" here:

  • Class1 is essentially a wrapper for Class2 (at least from the code you provided) - this behavior should be achieved by inheritance;
  • Class1 is very dependent on Class2 (Why does Class1, which is not a factory, deal with creating an instance of Class2?).

Should "Become" so:

public class Class2
{
    public RETURN_TYPE DoSmth()
    {
        // Что-то там делается
    }
}

public class Class1
{
    public RETURN_TYPE DoSmth(Class2 val)
    {
        /*
        Тут должна быть какая-то логика, но не тупой возврат, вроде:
        return val.DoSmth();
        */
    }
}

And it will be even better if you pass an instance of Class2 to the constructor of Class1 and access the object of Class2 in methods via the Class1 property-by the way, this is what the "law of Demeter" requires in this context. At the same time, thedependency should be passed not to the class itself, but to the interface that will implement Class2.

You partially answered the 3rd question yourself - the code becomes easier to maintain, support, write tests, code it becomes less difficult to understand, more opportunities for code reuse (otherwise the code will be duplicated), and reduces the dependence of classes on each other.

 1
Author: uorypm, 2016-05-17 08:48:42