User Tools

Site Tools


principles:principle_of_separate_understandability

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
principles:principle_of_separate_understandability [2013-10-07 10:21] – [Rationale] christianprinciples:principle_of_separate_understandability [2013-10-07 12:17] christian
Line 44: Line 44:
 When a module does not comply with PSU, this means that either a part of the functionality of the module does not belong here or the module has the wrong abstraction. So strategies for making a solution more compliant with PSU are: When a module does not comply with PSU, this means that either a part of the functionality of the module does not belong here or the module has the wrong abstraction. So strategies for making a solution more compliant with PSU are:
  
-  * Move the conflicting functionality to another module where it fits better (see [[Tell don't Ask/Information Expert|IE]], [[High Cohesion|HC]], and [[Model Principle|MP]]). +  * Move the conflicting functionality to another module where it fits better: [[refactorings:Move Field]], [[refactorings:Move Method]] (see [[Tell don't Ask/Information Expert|IE]], [[High Cohesion|HC]], and [[Model Principle|MP]]). 
-  * Build up a new module for the conflicting functionality (see [[High Cohesion|HC]]).+  * Build up a new module for the conflicting functionality: [[refactorings:Extract Method]], [[refactorings:Extract Class]] (see [[High Cohesion|HC]]).
   * Find the right abstraction for the module that allows the functionality to stay here (see [[Model Principle|MP]]).   * 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:Rename Module]] ([[Model Principle|MP]]).
  
 ===== Caveats ===== ===== Caveats =====
Line 140: Line 141:
   * First version: see ((http://www.objectmentor.com/resources/articles/xpepisode.htm)) or ((Robert C. Martin: //Agile Software Development, Principles, Patterns, and Practices//))   * First version: see ((http://www.objectmentor.com/resources/articles/xpepisode.htm)) or ((Robert C. Martin: //Agile Software Development, Principles, Patterns, and Practices//))
   * Second version: see ((http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata)) or ((http://www.slideshare.net/lalitkale/bowling-game-kata-by-robert-c-martin))   * Second version: see ((http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata)) or ((http://www.slideshare.net/lalitkale/bowling-game-kata-by-robert-c-martin))
 +
 +==== Example 3: Unnecessary State and Wrong Abstractions ====
 +
 +Have a look at the following piece of code:
 +<code java>
 +public String make(char candidate, int count)
 +{
 +    createPluralDependentMessageParts(count);
 +    return String.format("There %s %s %s%s", verb, number, candidate, pluralModifier);
 +}
 +</code>
 +What does it do? Certainly some information is missing. This piece of code is not separately understandable. You might feel the urge to ask for the implementation of ''createPluralDependentMessageParts'' as especially this method call is not separately understandable. OK, here it is:
 +
 +<code java>
 +private void createPluralDependentMessageParts(int count)
 +{
 +    if (count == 0)
 +    {
 +        thereAreNoLetters();
 +    }
 +    else if (count == 1)
 +    {
 +        thereIsOneLetter();
 +    }
 +    else
 +    {
 +        thereAreManyLetters(count);
 +    }
 +}
 +</code>
 +
 +Again, you most likely won't be satisfied and ask for the rest of the implementation:
 +
 +<code java>
 +public class Statistics2
 +{
 +     public static void main(String...args)
 +     {
 +         GuessStatisticsMessage statistics = new GuessStatisticsMessage();
 +         System.out.println(statistics.make('d', 0));
 +         System.out.println(statistics.make('d', 1));
 +         System.out.println(statistics.make('d', 25));
 +     }
 +
 +     static class GuessStatisticsMessage
 +     {
 +         private String number;
 +         private String verb;
 +         private String pluralModifier;
 +
 +         public String make(char candidate, int count)
 +         {
 +             createPluralDependentMessageParts(count);
 +             return String.format("There %s %s %s%s", verb, number, candidate, pluralModifier);
 +         }
 +
 +         private void createPluralDependentMessageParts(int count)
 +         {
 +             if (count == 0)
 +             {
 +                 thereAreNoLetters();
 +             }
 +             else if (count == 1)
 +             {
 +                 thereIsOneLetter();
 +             }
 +             else
 +             {
 +                 thereAreManyLetters(count);
 +             }
 +         }
 +
 +         private void thereAreNoLetters()
 +         {
 +             number = "no";
 +             verb = "are";
 +             pluralModifier = "s";
 +         }
 +
 +         private void thereIsOneLetter()
 +         {
 +             number = "1";
 +             verb = "is";
 +             pluralModifier = "";
 +         }
 +
 +         private void thereAreManyLetters(int count)
 +         {
 +             number = Integer.toString(count);
 +             verb = "are";
 +             pluralModifier = "s";
 +         }
 +     }
 +}
 +</code>
 +
 +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 ''thereIsOneLetter()'' does at the code is trivial. But you cannot understand //why// that code is there without knowing the rest. 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 might be an indicator for that. 
 +
 +The functionality is buried in the class which is most obvious with the ''pluralModifier''. This value is used to construct a plural form in case it is needed by appending it to another value in the ''String.format'' statement. The concept of making a plural form is not present in the code. Rather the code centers around assigning values to variables.
 +
 +A better solution might be the following:
 +
 +<code java>
 +public class Statistics3
 +{
 +     enum Number {SINGULAR, PLURAL}
 +
 +     public static void main(String...args)
 +     {
 +         Statistics3 statistics = new Statistics3();
 +         System.out.println(statistics.composeGuessStatistics('d', 0));
 +         System.out.println(statistics.composeGuessStatistics('d', 1));
 +         System.out.println(statistics.composeGuessStatistics('d', 25));
 +     }
 +
 +     private String composeGuessStatistics(char candidate, int count)
 +     {
 +         Number number = requiresPluralForm(count) ? Number.PLURAL : Number.SINGULAR;
 +         return String.format("There %s %s %s", thirdFormOfToBe(number), countToString(count), 
 +                 declineLetter(candidate, number));
 +     }
 +
 +     private boolean requiresPluralForm(int count)
 +     {
 +         return count != 1;
 +     }
 +
 +     private String thirdFormOfToBe(Number number)
 +     {
 +         return number == Number.SINGULAR ? "is" : "are";
 +     }
 +
 +     private String countToString(int count)
 +     {
 +         return count == 0 ? "no" : Integer.toString(count);
 +     }
 +
 +     private String declineLetter(char letter, Number number)
 +     {
 +         return number == Number.SINGULAR ? Character.toString(letter) : letter + "s";
 +     }
 +}
 +</code>
 +
 +Here virtually every piece of code is understandable on its own. 
  
 ===== Description Status ===== ===== Description Status =====
principles/principle_of_separate_understandability.txt · Last modified: 2021-10-18 22:13 by christian