12

I have a software patterns exam this week and one of the topics we are to study is Efferent and Afferent coupling.

I understand a package has a high Ce (efferent coupling) if it depends on a number of other types.

For example:

class Car{
    Engine engine;
    Wheel wheel;
    Body body;
}

This class would have a high efferent coupling because it depends on the Engine, Wheel and Body types.

Whereas the type "Wheel" would have a high Ca (afferent coupling) if several other packages depended on it (Car, Plane, Bicycle).

One of the possible questions on our exam is, when is Efferent / Afferent coupling good or bad? This seems odd to me because logically a program would need packages / classes with both high Efferent / Afferent coupling.

Does anybody have an example of when / where high efferent or afferent coupling is good / bad??

Thanks !

1
  • 5
    If only they had picked more confusing terms that sounded even more alike...
    – user949300
    Commented Dec 26, 2015 at 1:58

3 Answers 3

13

Afferent coupling can most easily be assessed in terms of how much pain it causes/saves because of change necessity or likelihood. For instance, take your wheel class and let's say that a lot of other modules use it to build various kinds of vehicles. If the wheel class is extremely stable, then this afferent coupling is beneficial since vehicles need wheels and they're using a reliable one. If, on the other hand, the wheel class is volatile in terms of maintenance, this afferent coupling is going to be a pain point as you introduce breaking changes repeatedly to lots of code.

Efferent coupling is similar in concept, but you're going to be looking at a slightly different value proposition. If you have a car class that depends directly upon a lot of individual parts (as opposed to say "Engine" and "Chassis" only, and they consist of other sub-parts), the class probably does a lot and may thus be a maintenance bottleneck. Changes to that class are likely to be difficult and risky because of its complexity. On the other hand, if the efferent coupling is high, but it's actually pretty cohesive and clear, then you don't have a hierarchy of objects and relationships to worry about.

When it comes to architecture/design, what you really have to consider are pretty much endless tradeoffs and these metrics are no different. If you want to figure out an example of something being good or bad, play the "what if" game. Imagine an example and say "what if I wanted to do X -- how much would that suck?" For X where the answer is "a lot," you have a disadvantage and for X where the answer is "that'd actually be really easy" you have an advantage.

5

Speaking in generalities, loose coupling:

positive: protects a portion of the system from changes in something that it depends on (afferent coupling)

negative: the relationship may be more difficult to understand

For example, if I were developing a system that relied on HTTTP, I would decide if I need to tightly or loosely couple to HTTP. If I thought that the system were likely to switch to a different protocol, I may choose to loosely couple to it, while if I accepted that HTTP were my protocol, I could tightly couple to that protocol for simplicity in understanding.

Consider that some of the complexities in WS* are in its decoupling from HTTP as a protocol.

2
  • Smart answer, but I don't see how it is related to the question, which was about efferent/afferent coupling and not tight/loose coupling.
    – lbalazscs
    Commented Aug 4, 2015 at 0:02
  • You are correct, @lbalazscs. No idea why I responded without answering the question!
    – jayraynet
    Commented Aug 5, 2015 at 9:54
2

Afferent

If something uses a bunch of different stuff (high number of afferent couplings), then it could be prone to break if any of those things change.

Instability = 1

enter image description here

Efferent

If something is used by a bunch of different stuff (high number of efferent couplings), then it could be prone to break a lot of things if it changes.

Instability = 0

enter image description here

Stability

Martin's definition of "stability" is an exotic blend between "difficult to change" and "having few reasons to change". Yet his instability metric only describes "difficulty of change". "Reasons to change" will have a lot more to do with factors that can't be easily calculated, like just designing your interfaces appropriately, at an appropriate level of abstraction, and understanding user-end requirements more clearly upfront.

So high efferent coupling with low afferent coupling yields stability (as in something difficult to change since it'll break a bunch of stuff), the opposite yields instability (as in something easy to change since it won't break a bunch of stuff).

A large number of afferent couplings could be an indicator that your design lacks focus -- it's using a whole bunch of different stuff so maybe it lacks a clear, singular responsibility.

A large number of efferent couplings could initially be interpreted as a really good thing, as it indicates that your design is being widely (re)used. Yet that would be bad if you feel tempted to change the design often in ways that breaks everything. So with a large number of efferent couplings comes the need for such packages to have "few or no reasons to change". The designs should be stable in the ideal sense of not having reasons to change, since they will likewise be very difficult to change.

Stable Abstractions Principle

Concepts like dependency inversion (which naturally calls for dependency injection) and SAP (stable abstractions principle) all suggest that dependencies flow towards abstractions. And there's a simple reason why when considering the "stability" in the context of having "few reasons to change". An abstract interface mentions no concrete details, it only focuses on "what to do" instead of "what things are", and thus has fewer reasons to change. The accelerated graphics port on our motherboards (abstract interface) has fewer reasons to undergo a design change than the GPU that plugs into it (a concrete detail).

Reusability vs. Reuse

A personal kind of metric of my own if I can suggest one which somewhat collides with Martin's is this notion I like to push that the most reusable libraries should seek to minimally reuse other code. That pushes instability towards a hard 0. It's for the practical reasons of having minimum reasons to change, but also to promote the easiest library to deploy. A general-purpose, widely-used library that depends on a dozen different libraries has a lot of reasons to change, as well as an awkwardly-bundled distribution which can be difficult to deploy. The difference here is that "reasons to change" in my case extends even to implementation, as it's coming from a library-oriented view that seeks to release stable versions of the library. Martin might discount the implementation as a very separate part, and only consider the metrics associated with the library's interface (which could be a whole lot more stable than its implementation).

From a distribution point of view, implementation and interface blur together to yield user dependencies to a stable or unstable library. From an interface point of view, only the interface is used and the implementation details associated are completely separate.

Not the answer you're looking for? Browse other questions tagged or ask your own question.