Clean Code · Clean code is simple and direct. Clean code reads like well-written prose. Clean code...

Post on 26-Aug-2020

18 views 7 download

Transcript of Clean Code · Clean code is simple and direct. Clean code reads like well-written prose. Clean code...

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.