Post: Tim & Jeff
Source: Uncle Bob
Font: Segoe Print
Coupling and cohesion are the two most important principles guiding the quality of an object-oriented class design. Most programmers learned about these principles in their first week of exposure to OO. The rise of TDD has helped reinforce the value of low coupling and high cohesion (although many programmers still unconsciously and even consciously resist truly small, cohesive classes and methods).
Uncle Bob teaches us that these core OO principles apply equally to packages. The Agile in a Flash card for Principles of Package Coupling covers the dependency side of the dynamic duo--how do you structure packages so as to improve the coupling relationships between them? This card covers the other side--how should you compose a package? What is the definition of "cohesive," as it applies to packages?
- The Reuse-Release Equivalence Principle (REP) - The REP tells us that classes should be packaged together because they are used together. This seems obvious, but many package structures are instead based around ideas like "functional areas," "architectural layers," or "originating team." The result? Users are inconvenienced, for example, by having to recite a litany of import lines at the top of each file. REP tells us to consider the destination instead of the source or even the structure of the code itself. It urges us to group things together for user convenience.
An entire system shoehorned into a single package/library would comply with this principle. But if the rate of change in the library was not extremely low, it would suffer from the problems addressed by the remaining principles.
- The Common-Reuse Principle (CRP) - This almost seems like a restatement of the REP, but the emphasis here is on limiting the impact to consumers. Imagine an API package with two sets of reusable class clusters. A programmer might choose to consume only one reusable component, but changes to the other component necessitate redistribution of the entire package. An unwanted release unnecessarily burdens the consuming programmer, who must re-integrate and re-test the entire library in order to stay current. Where the REP leads us to conglomerate, the CRP leads us to split packages apart. This tension between the principles leads us to find the level of granularity that provides greatest convenience (least negative impact) to users.
In spirit, the CRP is a package-level restatement of the Interface Segregation Principle, which says to keep interfaces small and focused for similar reasons.
- The Common-Closure Principle (CCP) - The CCP is an application of the Open-Closed Principle at the next level up. This principle suggests that you should group your classes around the impact of change. A change should optimally impact only one package; as many other packages as possible should be closed to that change. This varies from the other two principles as it recommends grouping classes around the way the code is maintained, rather than the way it is used. Modules with a high rate of change might need to be grouped by closure rather than by use. Highly stable packages might be better conglomerated (REP).
The principles of package cohesion are not absolute. Sometimes a packaging may satisfy all three principles at once, but often the principles represent competing principles. The development team will have to make trade-offs in order to find a balance that works. In general, smaller (and even smaller) packages provide the best chance for adherence to the CCP and CRP principles, although the REP says there is a limit to how small you will want to go.
Following these principles will require occasional re-packaging, which is upsetting to many users. However, correcting less-than-optimal packaging is a single deep cut that can halt the "death of a thousand paper cuts" caused when changes ripple across packages, or when users of a package have to deal with frequent irrelevant updates.