Quantcast
Channel: uttumuttu
Viewing all articles
Browse latest Browse all 10

Verbosity of Non-closured Java Violates the Law of Demeter

$
0
0
It is well known that Java programs (at least of the non-closured kind) tend to be rather verbose. Some people actually even prefer this, since it may make program more readable. In this post, however, I will try to show that verbosity isn't just a local issue. Rather, it affects code quality on a larger scale by increasing temptation to bad design - specifically, violating the law of demeter. Take the following simple Person class:

class Person {
    String name;
    int age;
}


Now assume we have a method containsName:

boolean containsName(List<Person> list, String name) {
    for(Person p : list) {
        if(name.equals(p.name)) {
            return true;
        }
    }
    return false;
}

Notice that the method is only interested in the names of the persons, so its dependency on class Person is merely accidental, unless we anticipate containsName to later utilize other details of class Person. In other words, methods like containsName tend to violate the law of demeter since they need to access the (sub(sub(sub)))fields of their arguments. This results in dreaded "train code", e.g., getInstance().getCompany().getPersons().first().getName().

Here's the version without the dependency:

boolean contains(List<String> xs, String y) {
    for(String x : xs) {
        if(x.equals(y)) {
            return true;
        }
    }
    return false;
}

Such contains method is actually already included in List, so the above method is redundant. However, calling this method is a nightmare since we must "extract" the names of the Persons manually:

List<String> names = new ArrayList();
for(Person p : persons) {
    names.add(p.name);
}
contains(names, "Mikko");

To summarize: the hardness of extracting container fields results in temptation to use less verbose code that violates the law of demeter.

Luckily, with closures, the above will become clearer:

contains(persons.map({Person p => p.name}), "Mikko")

or, equivalently

persons.map({Person p => p.name}).contains("Mikko")

To preserve performance, introducing another method taking a key closure
is warranted:

persons.find({Person p => p.name}, "Mikko")

Viewing all articles
Browse latest Browse all 10

Trending Articles