principles:principle_of_separate_understandability
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
principles:principle_of_separate_understandability [2013-01-15 17:09] – created christian | principles:principle_of_separate_understandability [2021-10-18 22:13] (current) – +++ restored +++ christian | ||
---|---|---|---|
Line 7: | Line 7: | ||
/* fill in contexts here: */ | /* fill in contexts here: */ | ||
* [[contexts: | * [[contexts: | ||
+ | * [[contexts: | ||
===== Principle Statement ===== | ===== Principle Statement ===== | ||
+ | |||
+ | Each module shall be understandable on its own---without knowing anything about other modules. | ||
===== Description ===== | ===== Description ===== | ||
+ | PSU means that: | ||
+ | * By looking at a class its purpose should be clear. | ||
+ | * By looking at the public methods of a class it should be clear why they are there. That means there should be no method that is only there because a specific other module needs it. | ||
+ | * By looking at the implementation of a module it should be clear how it works and why it was done that way. That means there should be no code that is solely there in order to make another module work. | ||
+ | * By looking at a private method it should be clear what it does. That means there should be no (private) method that is only meaningful in the context of another method (see [[#Example 2: Dependent Private Methods|example 2]]). | ||
+ | * By looking at a method invocation it should be clear what happens, why the parameters are there, and what they specify. It should not be necessary to look up the method implementation (see [[#Example 3: Unnecessary State and Wrong Abstractions|example 3]]). | ||
+ | * By looking at a single line of code it should be clear what it does without having to look up other code. | ||
===== Rationale ===== | ===== Rationale ===== | ||
+ | When a module is separately understandable, | ||
+ | |||
+ | An important | ||
+ | |||
+ | * You have to find the implementation and jump there (modern IDEs help here but it takes time nevertheless) | ||
+ | * While doing so, you have to memorize the call and the context of the call. If implementation and call are not colocated (which is preferable but not always possible) you won't see the call anymore so you have to memorize it. | ||
+ | * Then you have to read the code and [[glossary: | ||
+ | * If you could not memorize everything, you might have to jump back and forth to do the job. | ||
+ | * After you did all that you have to jump back and continue reading the method with the call you just mentally inlined. | ||
+ | |||
+ | In a nutshell, if you have to mentally inline code, it would have been better if it was already inlined. The [[refactorings: | ||
+ | |||
+ | Another point of view is that a violation of PSU either means that a part of the functionality does not belong to that module or the module has the wrong abstraction. So this is a sign of a design that needs improvement. | ||
===== Strategies ===== | ===== Strategies ===== | ||
+ | |||
+ | When a module does not comply with PSU, this means that either a part of the functionality of the module does not belong here (see [[#Example 1: Parsing Data|example 1]]) or the module has the wrong abstraction ([[#Example 3: Unnecessary State and Wrong Abstractions|example 3]]). So strategies for making a solution more compliant with PSU are: | ||
+ | |||
+ | * Move the conflicting functionality to another module where it fits better: [[refactorings: | ||
+ | * Build up a new module for the conflicting functionality: | ||
+ | * Find the right abstraction for the module that allows the functionality to stay here (see [[Model Principle|MP]]). | ||
+ | * Find a name which properly describes the abstraction of the module: [[refactorings: | ||
+ | |||
+ | ===== Caveats ===== | ||
+ | |||
+ | See section [[#contrary principles]]. | ||
===== Origin ===== | ===== Origin ===== | ||
+ | |||
+ | This principle is newly proposed in this wiki. Nevertheless it is believed that it is not " | ||
===== Evidence ===== | ===== Evidence ===== | ||
/* Comment out what is not applicable and explain the rest: */ | /* Comment out what is not applicable and explain the rest: */ | ||
+ | * [[wiki: | ||
+ | |||
/* | /* | ||
- | * [[wiki: | ||
* [[wiki: | * [[wiki: | ||
* [[wiki: | * [[wiki: | ||
Line 41: | Line 77: | ||
==== Contrary Principles ==== | ==== Contrary Principles ==== | ||
- | * [[principles:Keep It Simple Stupid]] | + | * [[Keep It Simple Stupid]] |
==== Complementary Principles ==== | ==== Complementary Principles ==== | ||
- | * [[principles:Information Hiding/ | + | * [[Information Hiding/ |
- | * [[principles:Low Coupling]] | + | * [[Model Principle]] (MP): The model contains the only information that should be necessary to understand the module. And if the abstraction of the model is wrong, MP helps getting it right. |
- | * [[principles:Model Principle]] | + | * [[Tell, don't Ask/ |
+ | * [[Low Coupling]] | ||
+ | * [[Single Level of Abstraction]] (SLA): The purpose of PSU is to avoid [[glossary: | ||
==== Principle Collections ==== | ==== Principle Collections ==== | ||
Line 54: | Line 92: | ||
- | ===== Example | + | ===== Examples |
+ | ==== Example 1: Parsing Data ==== | ||
+ | |||
+ | Suppose a program parses data stored in an spreadsheet file. There are three classes: | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | In such a scenario it might be convenient to simplify '' | ||
+ | |||
+ | This is a simple solution (see [[Keep It Simple Stupid|KISS]]) but it violates PSU. '' | ||
+ | |||
+ | A better solution (wrt. PSU) would be to give '' | ||
+ | |||
+ | ==== Example 2: Dependent Private Methods ==== | ||
+ | |||
+ | In a module that computes results in a bowling game there might be a method '' | ||
+ | |||
+ | <code java> | ||
+ | private int ball; | ||
+ | private int[] itsThrows = new int[21]; | ||
+ | | ||
+ | private boolean strike() | ||
+ | { | ||
+ | if (itsThrows[ball] == 10) | ||
+ | { | ||
+ | ball++; | ||
+ | return true; | ||
+ | } | ||
+ | return false; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Here the method not only computes if the current throw is a strike or not but also advances the counting variable '' | ||
+ | |||
+ | The following solution is better: | ||
+ | <code java> | ||
+ | private int rolls[] = new int[21]; | ||
+ | |||
+ | private boolean isStrike(int frameIndex) | ||
+ | { | ||
+ | return rolls[frameIndex] == 10; | ||
+ | } | ||
+ | </ | ||
+ | Here no counting variable is increased in some way. Furthermore this method does not rely on a correctly set private variable but gets a parameter. | ||
+ | |||
+ | This example is taken from Robert C. Martin. | ||
+ | * First version: see ((http:// | ||
+ | * Second version: see ((http:// | ||
+ | |||
+ | |||
+ | ==== Example 3: Unnecessary State and Wrong Abstractions ==== | ||
+ | |||
+ | This example is also inspired by Robert C. Martin. Have a look at the following piece of code from [[resources: | ||
+ | <code java> | ||
+ | public String make(char candidate, int count) | ||
+ | { | ||
+ | createPluralDependentMessageParts(count); | ||
+ | return String.format(" | ||
+ | } | ||
+ | </ | ||
+ | What does it do? Certainly some information is missing to answer this question. This piece of code is not separately understandable. You might feel the urge to ask for the implementation of '' | ||
+ | |||
+ | <code java> | ||
+ | private void createPluralDependentMessageParts(int count) | ||
+ | { | ||
+ | if (count == 0) | ||
+ | { | ||
+ | thereAreNoLetters(); | ||
+ | } | ||
+ | else if (count == 1) | ||
+ | { | ||
+ | thereIsOneLetter(); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | thereAreManyLetters(count); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Again, you most likely won't be satisfied and ask for the rest of the implementation: | ||
+ | |||
+ | <code java> | ||
+ | public class Statistics2 | ||
+ | { | ||
+ | | ||
+ | { | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | if (count == 0) | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | else if (count == 1) | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | verb = " | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | verb = " | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | verb = " | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Only if you read all that code, you really get what's going on. Also if you started with some other method, you would not understand it. It's clear what '' | ||
+ | |||
+ | The problem cannot be solved by moving or renaming methods or fields. The abstraction of the methods is wrong. The methods are just groupings of code and have no distinct meaning. The uncommon naming scheme of the methods lacking an imperative form of a verb might be an indicator for that. | ||
+ | |||
+ | The functionality is buried in the class which is most obvious with the '' | ||
+ | |||
+ | A better solution might be the following: | ||
+ | |||
+ | <code java> | ||
+ | public class Statistics3 | ||
+ | { | ||
+ | enum Number {SINGULAR, PLURAL} | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | |||
+ | | ||
+ | { | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Here virtually every piece of code is understandable on its own. | ||
===== Description Status ===== | ===== Description Status ===== | ||
/* Choose one of the following and comment out the rest: */ | /* Choose one of the following and comment out the rest: */ | ||
- | [[wiki: | + | /*[[wiki: |
/ | / | ||
- | /*[[wiki: | + | [[wiki: |
===== Further Reading ===== | ===== Further Reading ===== | ||
+ | * [[http:// | ||
+ | |||
+ | ===== Discussion ===== | ||
+ | |||
+ | Discuss this wiki article and the principle on the corresponding [[talk: | ||
principles/principle_of_separate_understandability.1358266150.txt.gz · Last modified: 2013-05-19 22:19 (external edit)