This article is more than 1 year old

Coupling and the power of accidental relationships

Emergent Design: break point

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.

More about

TIP US OFF

Send us news


Other stories you might like