Most read:
Popular archives:
Java Q&A Forums - Let the great migration begin
We're pleased to announce the first phase of the integration of the Java Q&A Forums with our community platform, JavaWorld's
Daily Brew. Whether you're one of our longtime forum users or a brand newbie, we hope you'll visit the Java Q&A Forums in their new home alongside JW Blogs.
| Enterprise AJAX - Transcend the Hype |
| Oracle Compatibility Developer's Guide |
As with last month's column, "Designing fields and methods," the principles discussed may be familiar to many readers, as they apply to just about any programming language. But given the vast quantity of code I have encountered in my career that didn't benefit from these basic principles, I feel it is an important public service to address the basics in the early installments of this column. In addition, I have attempted in this article to show how the basic principles apply in particular to the Java programming language.
Methods do things. On a low level, they do things such as accept data as input, operate on that data, and deliver data as output. On a higher level, they do things such as "clone this object," "print this string to the standard output," "add this element to the end of this vector," and "add this much coffee to this cup object."
Minimizing coupling (the topic of last month's article) requires you to look at methods on a low level. Coupling looks at how the inputs and outputs of a method connect it to other parts of the program. By contrast, maximizing cohesion requires that you look at methods on a high level. Cohesion looks at the degree to which a method accomplishes one conceptual task. The more a method is focused on accomplishing a single conceptual task, the more cohesive that method is.
The more cohesive you make your methods, the more flexible (easy to understand and change) your code will be. Cohesive methods help make your code more flexible in two ways:
int convertOzToMl(int ounces), which converts ounces to milliliters, is easier to comprehend at first glance than a method named int convert(int fromUnits, int toUnits, int fromAmount). At first glance, you could guess that the convert() method may be able to convert ounces to milliliters, but even if that were so, you would need to do more digging to find out what fromUnits value represents ounces and what toUnits value represents milliliters. The convertOzToMl() method is more cohesive than the convert() method because it does just one thing, and its name indicates what that thing is.As an example of a method that is not very functionally cohesive, consider this alternate way of designing a class that models coffee cups:
// In Source Packet in file:
// cohesion/ex1/CoffeeCup.java
// THIS APPROACH WORKS, BUT MAKES THE CODE
// HARD TO UNDERSTAND AND HARD TO CHANGE
public class CoffeeCup {
public final static int ADD = 0;
public final static int RELEASE_SIP = 1;
public final static int SPILL = 2;
private int innerCoffee;
public int modify(int action, int amount) {
int returnValue = 0;
switch (action) {
case ADD:
// add amount of coffee
innerCoffee += amount;
// return zero, even though that is meaningless
break;
case RELEASE_SIP:
// remove the amount of coffee passed as amount
int sip = amount;
if (innerCoffee < amount) {
sip = innerCoffee;
}
innerCoffee -= sip;
// return removed amount
returnValue = sip;
break;
case SPILL:
// set innerCoffee to 0
// ignore parameter amount
int all = innerCoffee;
innerCoffee = 0;
// return all coffee
returnValue = all;
default:
// Here should throw an exception, because they
// passed an invalid command down in action
break;
}
return returnValue;
}
}
CoffeeCup's modify() method is not very cohesive because it includes code to do tasks that, conceptually, are quite different. Yes, it is a useful
method. It can add, sip, and spill, but it can also perplex, befuddle, and confuse. This method is difficult to understand
partly because its name, modify(), isn't very specific. If you tried to make the name more specific, however, you would end up with something like addOrSipOrSpill(), which isn't much clearer.
Another reason modify() is hard to understand is that some of the data passed to it or returned from it is used only in certain cases. For example,
if the action parameter is equal to CoffeeCup.ADD, the value returned by the method is meaningless. If action equals CoffeeCup.SPILL, the amount input parameter is not used by the method. If you look only at the method's signature and return type, it is not obvious
how to use the method.

Figure 1: Passing control down to modify().
See Figure 1 for a graphical depiction of this kind of method. In this figure, the circle for the action parameter is solid black. The blackened circle indicates that the parameter contains data that is used for control. You can
differentiate data that is used for control from data that isn't by looking at how a method uses each piece of input data.
Methods process input data and generate output data. When a method uses a piece of input data not for processing, but for
deciding how to process, that input data is used for control.
To maximize cohesion, you should avoid passing control down into methods. Instead, try to divide the method's functionality among multiple methods that don't require passing down control. In the process, you'll likely end up with methods that have a higher degree of cohesion.