The Artima Developer Community
Sponsored Link

What's a Method to Do?
How to Maximize Cohesion While Avoiding Explosion
by Bill Venners
First Published in JavaWorld, April 1998

<<  Page 2 of 9  >>

Advertisement

Low cohesion
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.

Passing control down to modify()
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.

By the way, it is fine to pass data used for control back up from a method. (Throwing an exception is a good example of passing control up.) In general, up is the direction control should go: Data used for control should be passed from a method back to the method that invoked it.

<<  Page 2 of 9  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us