Select language

Example of coupled and decoupled code

In every programming book, you can find that two of the pattern that all well known authors really recommend is the depencency injection and the decoupled code.

Because you can find in all the programming books and on the internet what are these definitions, I would like to give you a little, an easy example of a little piece of coupled code, and the same but decoupled.

 

Imagine you try to calculate the average (mean) of the X coordinate of a list of Points. But, you also want to filter the points that are in \(\begin{equation}x = y = 0\end{equation}\) coordinates.

 

Well, I think that most of novice (and maybe not novice at all) programmers would do something like this

 

/**
* Do the average of the X coord
* @param points
* @return
*/
public static double calculateAverageCoupled(Collection<Point> points) { 
    double average = 0;
    double count = 0;
    for(Point p: points) {
        if(p.getX() != 0 &amp;&amp; p.getY() != 0) {
            average += p.getX();
            count++;
        }
    }
    return count != 0 ? average/count : 0;
}

 

In this method you are doing the filter, and the average in the same operation. This is maybe better for performance, but the code is coupled, you are doing two different operations in the same iteration, the filter, and the accumulation of the average.
What happend if you want to get the list of points you used for the average?
You have to go through the list again and re-do the filter. If an average only understand numbers, why do we have to pass Points? What happend if you want to change the filter condition, or make a new one?

Then, this is a way to do it in a decoupled way:

/** 
* Do the average of some double values
* @param points
* @return
*/
public static double calculateAverageDecoupled(Collection<Point> points) {
    Collection<Point> validPoints = new ArrayList<Point>(getValidPoints(points));
    Collection<Double> validXCoords = getXCoords(validPoints);
    return average(validXCoords);
}
public static Collection<Point> getValidPoints(Collection<Point> points) {
    Collection<Point> validPoints = new ArrayList<Point>();
    for(Point point : points) {
        if(point.getX() != 0 &amp;&amp; point.getY() != 0){  
            validPoints.add(point);
        }
    }
    return validPoints;
}
public static Collection<Double> getXCoords(Collection<Point> validPoints) {
    Collection<Double> validXCoords = new ArrayList<Double>(validPoints.size());
    for(Point p : validPoints) {
        validXCoords.add(p.getX()); 
    }
    return validXCoords; 
}

 

As you can see, here we have the code completely decoupled. First we do the filter, then we get a list of doubles with the x coordinates of that filtered points, and then, we can calculate the average using that list of doubles. Because we are using a list of doubles (that what an average is pretended to understand) we can use the apache math library, for example for doing the calculation.

One of the advantages of decupling code is that is easier to test, moreover, it makes the application more flexible. But, it doesn't come for free, as we say we degradate the performance, but also we need more memory.

Here is the same example, but using Guava library for the filter and transformation:

 

/**
* Do the average of some double values
* @param points
* @return
*/
public static double calculateAverageDecoupled(Collection<Point> points) {
    Collection<Point> validPoints = new ArrayList<Point>(getValidPoints(points));
    Collection<Double> validXCoords = getXCoords(validPoints);
    return average(validXCoords);
}
public static Collection<Point> getValidPoints(Collection<Point> points) {
    Predicate<Point> isValid = new Predicate<Point>() { 
        @Override
        public boolean apply(Point point) {
            return point.getX() != 0 &amp;&amp; point.getY() != 0;
        }
    };
    Collection<Point> validPoints = Lists.newArrayList(Iterables.filter(points, isValid));
    return validPoints;
}
public static Collection<Double> getXCoords(Collection<Point> validPoints) {
    Function<Point, Double> getXCoord = new Function<Point, Double>() {
        @Override
        public Double apply(Point point) {
            return point.getX();
        }
    };
    Collection<Double> validXCoords = Collections2.transform(validPoints, getXCoord);
    return validXCoords;
}

 

As you can see, we don't need comments in the code, because is self-explained. An we follow the KISS principle.

Add comment


Security code
Refresh