Original URL: https://www.theregister.com/2008/04/18/emergent_design_part_three/

Coupling and the power of accidental relationships

Emergent Design: break point

By Scott Bain

Posted in Channel, 18th April 2008 06:02 GMT

Book extract, part three In the third extract from his book, Emergent Design: The Evolutionary Nature of Professional Software Development published by Addison Wesley, Scott Bain tackles the complex issue of coupling, and looks at how unnecessary complexity can be injected through accidental or bad coupling.

Coupling is a general term that describes the situation where one part of a system affects another part of the system in some way. Often, in object orientation, we are talking about two classes - classes are coupled to the other if a change in the second class will cause a change (or a need for a change) in the first. Coupling is usually spoken of in terms of it being a problem, but in fact coupling is not bad in all cases.

In fact, some coupling is absolutely necessary, since a system with no coupling would not be a system at all. Coupling, in the good sense, is how individual objects are combined to create the overall system. We do not want coupling that:

Intentional coupling versus accidental coupling

Bad coupling is usually termed tight coupling. Logical, necessary, helpful coupling is called loose coupling. Loose coupling is ideal; tight coupling is to be avoided.

I am not wild about these terms, frankly. For one thing, they do not stick in my mind, and I always forget whether loose or tight is the bad one. They both sound bad, in a way loose things fall apart and tight things are hard to take apart and change.

My point is that part of what we are doing in promoting software development as a profession is getting terms like this more clearly defined, and to make sure they inform what we do.

To me, good coupling is coupling I (or you) put into the system on purpose, to achieve an end or solve a problem. The coupling that seems to "get me" is the coupling that I did not realize I had because it crept into the system without any intent on my part.

So, I use the terms "intentional" for good coupling and "accidental" for bad. It is not difficult to remember that accidental is bad - just think of a car wreck.

Coupling can occur at any level, from the code within a method to the architecture of the system overall. It can even occur in your driveway, as I shall explain.

Driveway coupling

I bought a new car a few years ago.

The next morning I went out to my car, got in, and started it. Once it was running, I tried to put it into reverse gear, but it would not go. The lever that selects from park, neutral, drive, and reverse would not move from park to reverse.

I was disappointed, and I called the dealer to explain the problem I was having. "Ah, yes, that always happens," he said. This was not what I wanted to hear. I had selected a car model that routinely broke its transmission? That was not what he meant. "People are always confused about this model... and we always forget to tell them for some reason. To put that car into gear, you need to put your foot on the brake."

Okay. So it would not have gone into drive or neutral either, since my foot was not depressing the brake pedal. I did not know that because I was not trying to go into anything but reverse (this is one reason beta testing is so difficult to get right). I was on a cell phone, so I tried it while the dealer was still on, and it worked.

"We sometimes just put our foot on the brake naturally when we start the car, so sometimes people do not run into this for a few days," he went on, "but invariably someone will call, within the first week, and report a failed transmission." "Why did they engineer it this way?" I asked. The designer in me was curious.

"It is to make sure your foot is not on the gas. People use their same foot for the gas and the brake, so if the foot is on the brake, it is not on the gas." he explained. That seemed to make sense.

But later I began to think. Why didn't they just hook up a sensor up to the gas pedal? I may or may not put my foot on the brake when I go into gear, but I certainly will not be accelerating at that point. In fact, that was the concern - that I not put the car into gear while revving the engine.

I reasoned that they did it because there was already a sensor on the brake pedal, for the brake light, and since there is no accelerate light, they would have had to add something to sense that the gas pedal was depressed.

This is bad coupling. The brake system and the transmission do not, logically, interconnect. My expectation did not include this as a possibility, and the reason the dealer forgets to tell people about this is that it is no more logical to him that it is to me. He does not remember.

I routinely forget to tell people about this rule when they are going to drive my car. It is not a rule that is explicitly part of braking or getting into gear. It was a kluge that was designed this way for the manufacturer's convenience. I imagine that someday, if my transmission needs to be fixed, the repairman will forget to hook up the line from the transmission to the brake pedal, and he too will think the thing is broken.

He will forget to hook it up because it is not expected, not intuitive, not logical that he should have to - even the dealer forgot, after all.

And this poor repairman will chase his tail for hours, trying to figure out why the car will not go into gear any more. And I will sympathize. I have had to maintain code.

Types of coupling

For emergent design, class coupling is going to be our primary concern. It can be broken down into four types.

Avoiding subclass coupling and making proper use of inheritance coupling is the subject of many of the design patterns we are going to explore a bit later, so we will leave that discussion for those chapters. However, identity and representational coupling are easy to illustrate. See the following code:

import java.util.*;
public class WidgetContainer {
  ArrayList myWidgetList = new ArrayList();

  public void addWidget(Widget aWidget) {
    myWidgetList.add(aWidget); 
  }

  public Iterator getWidgets() {
    return myWidgetList.iterator();
  }

}

public class Widget {
  public void widgetyBehavior() {
    System.out.println("Just being a Widget...");
  }

  public void storeSelf(WidgetContainer wc) {
    wc.addWidget(this);
  }
}

public class WidgetClient{
  public static void main(String[] args) {
    WidgetContainer myWidgetContainer = new WidgetContainer();
    for(int i=0; i<10; i++) {
      // Make a new widget, and have it store itself in the
      // container
      new Widget().storeSelf(myWidgetContainer);
    }
  }
}

Here we have WidgetClient creating 10 shiny new widgets, and then telling them to store themselves in a WidgetContainer. WidgetContainer has identity coupling to Widget. You can see this because of the following method references the type Widget:

 public void addWidget(Widget aWidget)

The parameter aWidget is declared to be of type Widget. But none of the methods on any aWidget are ever called, so there is no representational coupling here. Change Widget all you want, and WidgetContainer does not mind, but remove Widget entirely and WidgetContainer breaks. This is why it is called identity coupling, because WidgetContainer is coupled to the existence of Widget. It comes to us from the mathematical principle of identity (a = a). This coupling is good, logical, explicit; I like to call it intentional, because it makes perfect sense for a type-safe container of something to know that the something exists, and what its type is.

WidgetClient has identity coupling to both WidgetContainer and Widget, which also makes sense since it's using one to contain the other. It creates a single WidgetContainer, and then creates a series of widgets and calls the storeSelf() method on each. But then we see some oddities. The table below shows all the coupling we find.

Class Coupled to Type of coupling
Widget WidgetContainer Identity and Representational
WidgetContainer Widget Identity
WidgetClient Widget Identity and Representational
WidgetClient WidgetContainer Identity

Widget has identity and representational coupling to WidgetContainer. That is unexpected, since there is really no reason for the contained to know about the container.

Widget not only knows that WidgetContainer exists, but if you examine the code you will see that in Widget's storeSelf() method it calls the addWidget() method on the WidgetContainer that was passed in to the method, so there is coupling to that interface as well (which is representational coupling, coupling to the public face of another object, the way it represents itself to the outside world).

Furthermore, WidgetClient does not have representational coupling to WidgetContainer (it just makes one), but does have representational coupling to Widget (it calls the storeSelf() method on Widget). That is odd, because logically, if WidgetClient is using a WidgetContainer to store a Widget, we would expect WidgetClient to have representational coupling to the thing it is using, namely WidgetContainer.

Remember that we want our system to be as logical and as sensible as possible because we may have to figure out how it works six months from now, when much of our current understanding has been forgotten. Finally, we have a bidirectional couple here: WidgetContainer and Widget have identity coupling to each other. In general, any time I see a bidirectional coupling like this, I start to get suspicious about the design, and wonder if it could be decoupled. Bidirectional coupling is especially bad because neither of the classes can be changed (or in this case, eliminated) without affecting the other.

The following code illustrates a better way:

import java.util.*;
public class WidgetContainer {
  ArrayList myWidgetList = new ArrayList();

  public void addWidget(Widget aWidget) {
    myWidgetList.add(aWidget); 
  }

  public Iterator getWidgets() {
    return myWidgetList.iterator();
  }

}

public class Widget {
  public void widgetyBehavior() {
    System.out.println("Just being a Widget...");
  }
}

public class WidgetClient{
  public static void main(String[] args) {
    WidgetContainer myWidgetContainer = new WidgetContainer();
    for(int i=0; i<10; i++) {
      myWidgetContainer.addWidget(new Widget());
    }
  }
}

The coupling now is explicit, logical, expected. Let's see who is coupled to whom and how. Take a look at next table. Widget is coupled to nothing at all. You can change anything you like in this system, and Widget will not break.

There is no bidirectional coupling here.

Class Coupled to Type of coupling
Widget Nothing None
WidgetContainer Widget Identity
WidgetClient Widget Identity
WidgetClient WidgetContainer Identity & representational

The only representational coupling that exists is in WidgetClient, which has representational coupling to WidgetContainer. Because we said all along that it uses the container to store its widgets, we would expect it to have representational coupling, and it does. This is not hard to remember, or to reconstruct in the future, because it makes sense that it works this way. Widget now only fulfills its widgetyBehavior().

Making objects responsible for themselves can be a misleading prescription, because it can lead to methods like storeSelf() in the first version of Widget. The problem lies in determining who is responsible for what. Storing is not part of the widget-ish behavior, so it is not part of Widget's responsibility. If Widget does not know about the WidgetContainer, we can use Widgets in scenarios where they are stored, not stored, stored in different ways, whatever, without changing Widget.

Try compiling the first version of Widget without a WidgetContainer in your project, and it does not compile. The second one does, because it is not coupled to the identity of WidgetContainer. In the first version of Widget, you would never be able to put Widget into a project without WidgetContainer, even if you were not using the container for anything.

That alone should illustrate why it is bad, or accidental, coupling.

Next time: redundancy, testability and readability.

This chapter is excerpted from the new book, Emergent Design: The Evolutionary Nature of Professional Software Development by Scott Bain, published by Addison-Wesley Professional, March 2008 ISBN 0-321-50936-6 Copyright (c) 2008 Pearson Education, Inc. For more information, please see informIT.com and Register Books.