Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Fear of Decoupling

DZone's Guide to

Fear of Decoupling

When it comes to object-oriented programming, are you afraid of decoupling your objects? Click here to find out more about objects with multiple interface methods.

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

Objects talk to each other via their methods. In mainstream programming languages, like Java or C#, an object may have a unique set of methods together, with some methods it is forced to have because it implements certain types, also known as interfaces. My experience of speaking with many programmers tells me that most of us are pretty scared of objects that implement too many interface methods. We don't want to deal with them since they are polymorphic and, because of that, unreliable. It's a fair fear. Let's try to analyze where it comes from.

Funny Games (2007) by Michael Haneke

As usual, let's start with a simple Java example. Here is the amount of money I'm going to send to a user via, say, the PayPal API:

interface Money {
  double cents();
}


Now, here I am with the method that sends the money:

void send(Money m) {
  double c = m.cents();
  // Send them over via the API...
}


These two pieces of code are, as we call it, loosely coupled. The method send() has no idea which class is provided and how exactly the method cents() is implemented. Maybe it's a simple constant object of one dollar:

class OneDollar implements Money {
  @Override
  double cents() {
    return 100.0d;
  }
}


Or, maybe, it's a way more complex entity that makes a network connection first, in order to fetch the current USD-to-EUR exchange rate, update the database, and then return the result of some calculation:

class EmployeeHourlyRate implements Money {
  @Override
  double cents() {
    // Fetch the exchange rate;
    // Update the database;
    // Calculate the hourly rate;
    // Return the value.
  }
}


The method send() doesn't have the knowledge of what exactly is provided as its first argument. All it can do is hope that the method cents() will do the work right. But, what if it doesn't?

If I'm a developer of the method send() and I'm fully prepared to take the blame for the mistakes my method causes, I do want to know what my collaborators are. And, I want to be absolutely sure they work — not just that they work, but that they work exactly how I expect them to. Preferably, I would like to write them myself to ensure that nobody touches them after I have implemented them. You get the sarcasm, right?

This may sound like a joke, but I have heard this argument many times. They say that "it's better to be completely sure two pieces work together, instead of relying on the polymorphism and then spending hours debugging something I didn't write." And, they are right, you know. Polymorphism — when a seemingly primitive object of type Money does whatever it wants, including HTTP requests and SQL UPDATE queries — doesn't add reliability to the entire application, does it?

No, it doesn't.

Obviously, polymorphism makes the life of the developers of this type Money and its "ancestors" way simpler, since they don't have to think about their users much. All they worry about is how to return the double when cents() is called. They don't need to care about speed, potential exceptions, memory usage, and many other things since the interface doesn't require that. It only tells them to return the double and call it a day. Let somebody else worry about everything else. Easy, huh? "But that's a childish and egoistic way of thinking," you might say!

Yes, it is.

However, you've most definitely heard of the Fail Fast idea. In a nutshell, this idea claims that in order to make an application robust and stable, we have to make sure its components are as fragile as possible and as vulnerable as they can be in response to any potential exceptional situation. They have to break whenever they can and let their users deal with the failures. With such a philosophy, no object will assume anything good about its counterparts and will always try to escalate problems to higher levels, which eventually will hit the end user, who will then report back to the team. The team will fix them all and the entire product will stabilize.

If the philosophy is the opposite and every object is trying to deal with problems on its individual micro level, the majority of exceptional situations will never be visible to users, testers, architects, and programmers who are supposed to be dealing with them and finding solutions for them. Thanks to this "careful" mindset of individual objects, the stability and robustness of the entire application will suffer.

We can apply the same logic to this "fear of loose coupling."

When we worry about how Money.cents() works and want to control its behavior, we are doing ourselves and the entire project a huge disservice. In the long run, we destabilize the product, instead of making it more stable. Some even want to prohibit polymorphism by declaring the method send() , as shown below:

void send(EmployeeHourlyRate m) {
  // Now I know that it's not some abstract Money,
  // but a very specific class EmployeeHourlyRate, which
  // was implemented by Bobby, a good friend of mine.
}


Here, we limit the number of mistakes our code may have, since we know Bobby, we've seen his code, we know how it works, and which exceptions to expect. We are safe. For now. But, strategically speaking, by not allowing our software to make all possible mistakes and throw all possible exceptions in all unusual situations, we are seriously limiting its ability to be properly tested, and that's why it's destabilized.

As I mentioned earlier, the only way to increase the quality of software is to find and fix its bugs. The more bugs we fix, the fewer bugs that remain hidden and not fixed. A fear of bugs and our intention to prevent them is only shooting us in the foot.

Instead, we should let everybody, not only Bobby, implement Money and pass those implementations to send(). Yes, some of them will cause troubles and may even lead to UI-visible failures. But if our management understands the concept of software quality right, they will not blame us for mistakes. Instead, they will encourage us to find as many of them as possible, reproduce them with automated tests, fix, and re-deploy.

Thus, the fear of decoupling is nothing else but Fail Safe.

Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:
coupling vs reliability ,loose coupling ,interfaces ,objects ,java ,decoupling

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}