PHPnews.io

New Metric: the Distance of Coupling

Written by Yegor Bugayenko / Original link on Oct. 27, 2020

Encapsulation, as you know, is one of the four key principles in object-oriented programming. Encapsulation, according to Grady Booch et al., is “the process of hiding all the secrets of an object that do not contribute to its essential characteristics.” Practically speaking, it’s about those private attributes that we use in Java and C++: they are not visible to the users of our objects, that’s why they can’t be modified or even read. Booch et al. believe that the purpose of encapsulation is “to provide explicit barriers among different abstractions,” which leads to “a clear separation of concerns.” However, does it really work as planned? Do we really have explicit barriers between objects? Let’s see.

chacun-sa-vie.jpg
Chacun sa vie (2017) by Claude Lelouch

First, I’m not the first and not the only one asking this question. David West much earlier said that “in most ways, encapsulation is a discipline more than a real barrier,” and that “seldom is the integrity of an object protected in any absolute sense”. In practice, “it is up to the user of an object to respect that object’s encapsulation.’’ Indeed, let’s take a look at the class Temperature from my blog post about naked data:

class Temperature {
  private int t;
  public int getT() { return this.t; }
  public void setT(int t) { this.t = t; }
}

Can we say that the attribute t is truly encapsulated? Technically, it is: it’s impossible to modify it directly via the dot notation. Simply put, we can’t do this:

Temperature x = new Temperature();
x.t = 10;

And we can’t even do this:

int y = x.t;

However, we can do exactly the same via the getter getT() and the setter setT(). Thus, the designer of the class Temperature gives us the ability to access its attribute, but indirectly, through getters and setters. I would say that the principle of encapsulation is being violated here, and, I’m sure, Allen Holub would agree with me. What is the solution? The article about naked data proposed the use of the TellDontAsk principle and that we should get rid of the getter:

class Temperature {
  private int t;
  public String toString() {
    return String.format("%d F", this.t);
  }
}

Now the class Temperature doesn’t allow us to read its attribute t. Instead, we can only tell it to prepare a string presentation of the temperature and return that back to us. Maybe not exactly a classic example of the “tell” paradigm, since some data is coming back, but now it looks much better than before. The beauty of this refactoring is less coupling between the client and the object. With the getter (or direct access to the attribute via dot notation), the client was able to retrieve the numeric value of the temperature and recalculate it in Fahrenheit, assuming that it was in Celsius. With the String being returned the client would not do this. The string would only be used as a final product, not modifiable. Or maybe not?

What if the client does this:

Temperature x = new Temperature();
String txt = x.toString();
String[] parts = txt.split(" ");
int t = Integer.parseInt(parts[0]);

How does it look now? Isn’t this a violation of encapsulation? The result of toString() is not treated as it is supposed to be treated. Not as a solid string, but as data with some internal structure, which is known to the client. The knowledge the client possesses about the output is the key problem here. The client knows too much and uses this knowledge for its own benefit: to deconstruct the data and manipulate the result.

Can we really prohibit the client from doing this? There is no such feature in any programming language, to my knowledge. When the output of the method is delivered to the client, the client is allowed to do whatever is needed with it. This is that lack of respect to encapsulation, if I correctly understood Dr. West. And we are not even discussing the Reflection API, which would allow us to take the t out of Temperature without even calling any methods.

Thus, encapsulation is not an explicit barrier. It exists for as long as we have the desire to respect it. If we don’t, nothing can stop us from abusing an object in any way we want. And even private attribute modifiers won’t help. Moreover, they will only create an illusion of encapsulation, while in reality everyone is able to do whatever they feel is suitable for their business case.

I have a proposal, though, which may help us make encapsulation more explicit.

What if we had the ability to control what’s happening with the data and objects after we return them to clients? What if we could prevent the client from doing a split() on the output of the toString() method? We could do this at compile time, I think, by going through all the code and checking how far the moments of interaction with our objects are from the places where they were returned. In the example above, the distance is two: 1) first, we do the split, 2) second, we do the parseInt(). Larger applications will have bigger numbers, of course.

It seems that we can use this distance number as a metric for coupling between objects in the entire app. The larger the number (or the mean of all numbers), the worse the design: in good design we are not supposed to take something out of a method and then do some complex processing. We are, according to the TellDontAsk principle mentioned above, supposed to let our objects do the work and only return a quick summary of it. The distance metric will tell us exactly that: how many times, and by how much, we violated the principle of loose coupling.

Would you be interested in creating such an analyzer for, say, Java code?

oop yegor256

« Learn about GitHub Actions and Unconventional Autoloaders in tomorrow’s Laravel Worldwide Meetup - SymfonyWorld 2020: All about the conference experience »