clean code · clean code is simple and direct. clean code reads like well-written prose. clean code...
TRANSCRIPT
Clean Code
a short summary of
The Future of Code
• Model-based Development
• Automatic Code Generation
• Scratch (MIT)
• Domain Specific Languages
• Code vs. Specification
Why should I care about Clean Code?
• Bad Code decreases productivity.• Management: “We simply hire more staff !”
– unfamiliar with code– no clear design– � mess increases
[1]
Apropos Manpower: Brooks’s Law
Adding manpower
to a late software project
makes it later.
[Fred Brooks,The Mythical Man-Month]
Turing-PreisträgerFrederick P. Brooks Jr. (* 1931)
Bild: Wikipedia
Now, what is Clean Code?
Clean code is simple and direct. Clean code
reads like well-written prose. Clean code never
obscures the designer’s intent but rather is full of
crisp abstractions and straightforward lines of
control.
Grady Booch (UML)
Namingpublic List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] == 4)
list1.add(x);
return list1;
}
Namingpublic List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard)
if (cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
Namingpublic List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
Namingpublic List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
BTW: This is the same in Scala! ;-)
List[Cell] getFlaggedCells = gameBoard.take(_.isFlagged)
Code Smells
• Kommentare– Wenn Code dokumentiert werden muss:
Code neu schreiben (Variablen, Funktionen umbenennen)– Einrückung & Formatierung– Class Header– C2: Obsolete Comment
• Code u. Kommentare müssen übereinstimmen
– C3: Redundant Comment• Vermeide: i++; //increment i
– C4: Poorly Written Comment• Rechtschreibung, Grammatik, Satzzeichen
– C5: Commented-Out Code• Löschen (Versionskontrolle!)
Code Smells (2)
• Methoden
– Benennung: was macht die Methode? � Verb
– 1 Methode: 1 Aufgabe � ansonsten aufteilen
– Achtung auf Sideeffects
– 1 Methode: 1 Abstraktionsniveau
– Wenig Argumente (F1)
– Dead Code löschen (Versionskontrolle) (F4)
– Corner cases bedenken und testen (G3)
Code Smells (3)
• Code Duplication (G5)– Widerspricht DRY (Andy Hunt). “Once, and only once”
(K.Beck)
– Arten• Gleicher/Ähnlicher Code in einer Klasse
– Extrahiere eine Methode
• Gleicher/Ähnlicher Code in Subklassen– Ziehe den Code in die Basisklasse
• Gleicher/Ähnlicher Code in verschiedenen Klassen– Extrahiere den Code und erstelle eine Hilfsklasse
– Design-Patterns helfen (sh. später)
Code Smells (4)
• Code at Wrong Level of Abstraction (G6)– Good software design requires that we separate concepts at different
levels and place them in different containers.
– Container: class, file, module, component
public interface Stack {
Object pop() throws EmptyException;
void push(Object o) throws FullException;
double percentFull();
class EmptyException extends Exception {}
class FullException extends Exception {}
}
�Difficult in general!• Base Class Depending no their Derivatives (G7)
– Basisklassen sollen nichts über Subklassen wissen
Code Smells (5)
• Too much information (G8)– Öffentliche Schnittstelle klein halten– Wenig preis geben, Implementierung verstecken
• Dead Code (G9)– If, switch/case, try/catch– Mit Hilfe von Tools erkennbar � Code löschen
• Vertical Separation (G10)– Lokale Variablen dort deklarieren wo sie gebraucht werden
• Clutter (G12)– Leerer Default-Konstruktor– Ungenützte Variablen, Funktionen
Code Smells (5b)
• Use Explanatory Variables(G19)
– Lesbarkeit durch Hilfsvariablen erhöhen.
• Auch um Anzahl von Casts zu reduzieren (sh. später)
Matcher match = headerPattern.matcher(line);
if(match.find())
{
String key = match.group(1);
String value = match.group(2);
headers.put(key.toLowerCase(), value);
}
Code Smells (6)
• Follow Standard Conventions (G24)– Lege für das Team Richtlinien fest und kommuniziere sie
– Coderrichtlinien (Code Conventions) geben vor:• Namenwahl (z.B. Klassennamen als Substantive in CamelCase, …)
• Strukturierung (z.B. Einrückungen, Zeilenumbruch, max. Zeilenlänge, …)
• Kommentierung (z.B. Alle Kommentare in Englisch, Aufbau,…)
– Zusätzlich• Äußere Form (z.B. GUI Design, Style Guide, erlaubte Controls, …)
• Bibliotheken (z.B. Erlaubte Libraries, Versionen, …)
• Compiler-Einstellungen (z.B. Nur Release Versionen,…)
Code Smells (7)
• Replace Magic Numbers with Named Constants (G25)– Zahlen und Strings:private final int WIDTH = 80;
private final String CONFIG_FILE = “config.xml”
• Encapsulate Conditionals (G28)– Gut benannte Hilfsvariablen/-methoden einführen:
if (timer.hasExpired() && !timer.isRecurrent())
vs.
if (shouldBeDeleted(timer))
Code Smells (8)
• Functions Should Descend Only One Level of Abstraction (G34)
Code Smells (9)
– Keep Configurable Data at High Levels (G35)
public static void main(String[] args) throws Exception
{
Arguments arguments = parseCommandLine(args);
...
}
public class Arguments
{
public static final String DEFAULT_PATH = ".";
public static final String DEFAULT_ROOT = "FitNesseRoot";
public static final int DEFAULT_PORT = 80;
public static final int DEFAULT_VERSION_DAYS = 14;
...
}
Code Smells (10)
– Avoid Transitive Navigation (G36)• = Write “shy code” [The Pragmatic Programmer, AW, 2000].
• Module sollen nur über ihre direkten Kollaborator-Module bescheid wissen müssen, nicht über die Verflechtung des gesamten Systems.
Statt a.getB().getC().doSomething();
� myCollaborator.doSomething();
Code Smells (11)
– Choose Descriptive Names (N1)• Lesbarkeit zu 90% von Namen abhängig
• Sinnvolle Bezeichnung wählen und konsistent halten
– Use Long Names for Long Scopes
• Je länger eine Variable gültig ist desto länger und aussagekräftig sollte ihr Name sein
• Der Klassiker i ist duraus ok für Schleifenvariablen
Code Smells (11’2)public int x() {
int q = 0;
int z = 0;
for (int kk = 0; kk < 10; kk++) {
if (l[z] == 10) {
q += 10 + (l[z + 1] + l[z + 2]);
z += 1;
} else if (l[z] + l[z + 1] == 10) {
q += 10 + l[z + 2];
z += 2;
} else {
q += l[z] + l[z + 1];
z += 2;
}
}
return q;
}
public int score() {
int score = 0;
int frame = 0;
for (int frameNr = 0; frameNr < 10; frameNr ++) {
if (isStrike(frame)) {
score += 10 + nextTwoBallsForStrike(frame);
frame += 1;
} else if (isSpare(frame)) {
score += 10 + nextBallForSpare(frame);
frame += 2;
} else {
score += twoBallsInFrame(frame);
frame += 2;
}
}
return score;
}
Code Smells (12)
– „Public“ everywhere• Public: nur was wirklich „von aussen“ sichtbar sein soll;
Felder, Methoden, aber auch ganze Klassen, Konstruktoren
• Rest: private oder default-modifier
– Excessive Casting
Code Smells (12’2)private void changePortAlignment(Object value) {
switch ((int)value) {
case 0:
((Port) _element).setIndex(((Actor)((Port)
_element).getParent()).getCountNorth() + 1);
break;
case 1:
((Port) _element).setIndex(((Actor)((Port)
_element).getParent()).getCountSouth() + 1);
break;
//..
default:
break;
}
((Port) _element).setAlignment((int) value);
}
Code Smells (12’3)private void changePortAlignment(Object value) {
Port port = (Port) _element;
Actor parent = (Actor) port.getParent();
switch ((int)value) {
case NORTH:
port.setIndex(parent.getCountNorth() + 1);
break;
case SOUTH:
port.setIndex(parent.getCountSouth() + 1);
break;
//..
default:
break;
}
port.setAlignment((int) value);
}
… with some aux. Variables (G19) and named constants (G25)
Code Smells (12’4)private void changePortAlignment(Object value) {
Port port = (Port) _element;
Actor parent = (Actor) port.getParent();
Alignment align = Alignment.valueOf(value.toString());
port.setIndex(parent.getCountAlign(align));
port.setAlignment(align);
}
public enum Alignment {EAST, NORTH, SOUTH, WEST};
… with some more restructuring and enums
Code Smells (13)
• Exception Handling
Code Smells
Making code readable requires a dedication
to continuous improvement.