September 28, 2008

Coupling & Cohesion, Part II

The concepts of coupling and cohesion apply at all levels of programming from writing a single method all the way up to planning the architecture of Amazon.com. As you build each piece (an individual line of code, a method, an object, or an entire remote service), you have to make sure it has strong cohesion and loose coupling. Does the component do exactly one thing which is easy to describe and conceptualize? Does it have relatively few, easy-to-understand connections to the other components around it?

Consider what it means to have strong cohesion for a single line of code. To have good cohesion, it would need to produce a single clear outcome. On the other hand, a line of code with poor cohesion will tend to have multiple side effects, or calculate many values at once:


int balance = priorBalances[balanceIndex++] -
withdrawals[withdrawalIndex++];

float gravitation = UNIVERSAL_G * (bodyA.mass * KG_PER_LB) *
(bodyB.mass * KG_PER_LB) /
((bodyA.position.x - bodyB.position.x) *
(bodyA.position.x - bodyB.position.x) +
(bodyA.position.y - bodyB.position.y) *
(bodyA.position.y - bodyB.position.y));


In both of these cases, the code is doing multiple things at once, and in order to understand what is going on, you have to mentally pull it apart, understand each piece, and then integrate them back together. Both of these lines can easily be re-written as several lines which each demonstrate much better cohesion:


balanceIndex++;
withdrawalIndex++;
int balance = priorBalances[balanceIndex] - widthdrawals[withdrawalIndex];

float massA = bodyA.mass * KG_PER_LB;
float massB = bodyB.mass * KG_PER_LB;
float xRadiusPart = bodyA.position.x - bodyB.position.y;
float yRadiusPart = bodyA.position.y - bodyB.position.y;
float radiusSquared = xRadiusPart * xRadiusPart + yRadiusPart *
yRadiusPart;
float gravitation = UNIVERSAL_G * massA * massB /
radiusSquared;


Each of the re-written examples has statements which are simpler, easier to understand, and clearly accomplish a single result.

At the far other end of the size spectrum, consider what strong cohesion means for a single service in a massively distributed system (e.g. Amazon.com). For a long while, there was a single, central piece of software, called Obidos, which was responsible for everything from presenting HTML to calculating the cost of an order, to contacting the UPS server to find out where a package was. This ultimately resulted in single program which constantly broke down, was impossible to understand fully, and nearly impossible to actually compile. The crux of the problem is that Obidos tried to do too much, and wound up with terrible cohesion. There was no way anyone could get their head around the essential functions it performed without dropping all kinds of important information.

That was many years ago, and since then, Amazon has considerably improved its situation. As an example, there is now a single service whose sole purpose is to compose the totals and subtotals for an order. It communicates with other services which each compute individual charges (e.g. shipping charges, tax, etc), and all it does is put them together in the right order. This new service is much easier to understand, far easier to describe, and much, much easier to work with on a daily basis.

Next time, I'll talk about how coupling applies across all levels, too.

No comments:

Post a Comment