Object-Oriented Design – Understanding Loose Coupling

couplingdesigndesign-principlesobject-oriented-design

I am trying to learn GRASP and I found this explained (here on page 3) about Low Coupling and I was very surprised when I found this:

Consider the method addTrack for an Album class, two possible methods are:

addTrack( Track t )

and

addTrack( int no, String title, double duration )

Which method reduces coupling?
The second one does, since the class using the Album class does not have to know a Track class.
In general, parameters to methods should use base types (int, char …) and classes from the java.* packages.

I tend to diasgree with this; I believe addTrack(Track t) is better than addTrack(int no, String title, double duration) due to various reasons:

  1. It is always better for a method to as fewer parameters as possible (according to Uncle Bob's Clean Code none or one preferably, 2 in some cases and 3 in special cases; more than 3 needs refactoring – these are of course recommendations not holly rules).

  2. If addTrack is a method of an interface, and the requirements need that a Track should have more information (say year or genre) then the interface needs to be changed and so that the method should supports another parameter.

  3. Encapsulation is broke; if addTrack is in an interface, then it should not know the internals of the Track.

  4. It is actually more coupled in the second way, with many parameters. Suppose the no parameter needs to be changed from int to long because there are more than MAX_INT tracks (or for whatever reason); then both the Track and the method need to be changed while if the method would be addTrack(Track track) only the Track would be changed.

All the 4 arguments are actually connected with each other, and some of them are consequences from others.

Which approach is better?

Best Answer

Well, your first three points are actually about other principles than coupling. You always have to strike a balance between oft-conflicting design principles.

Your fourth point is about coupling, and I strongly agree with you. Coupling is about the flow of data between modules. The type of the container that data flows in is largely immaterial. Passing a duration as a double instead of as a field of a Track doesn't obviate the need to pass it. The modules still need to share the same amount of data, and still have the same amount of coupling.

He is also failing to consider all the coupling in the system as an aggregate. While introducing a Track class admittedly adds another dependency between two individual modules, it can significantly reduce the coupling of the system, which is the important measure here.

For example, consider an "Add to Playlist" button and a Playlist object. Introducing a Track object could be considered to increase coupling if you only consider those two objects. You now have three interdependent classes instead of two. However, that is not the entirety of your system. You also need to import the track, play the track, display the track, etc. Adding one more class to that mix is negligible.

Now consider needing to add support for playing tracks over the network instead of just locally. You just need to create a NetworkTrack object that conforms to the same interface. Without the Track object, you would have to create functions everywhere like:

addNetworkTrack(int no, string title, double duration, URL location)

That effectively doubles your coupling, requiring even modules that don't care about the network-specific stuff to nevertheless still keep track of it, in order to be able to pass it on.

Your ripple effect test is a good one to determine your true amount of coupling. What we are concerned with is limiting the places a change affects.