Clean, effective java

Preview:

DESCRIPTION

In deze presentatie die ik gaf aan collega's, licht ik enkele topics toe uit deze boeken: - Robert C. Martin, Clean Code - Joshua Bloch, Effective Java

Citation preview

Clean, effective Java

Bert Van Vreckem

2

3

?

4

(TODO)

5

6

7

Josh Bloch

8

Inhoud EJ (2nd Ed, 2008)

● Creatie objecten● Gemeensch. methods● Klassen en interfaces● Generieke klassen● Enums, annotaties● Methods

● Algemene programmeertips

● Foutafhandeling● Concurrency● Serialisatie

● 78 “items” (EJ#nn)

9

10

Robert 'Uncle Bob' Martin

11

Inhoud CC(2009)

● Naamgeving● Functies● Commentaar● Formattering● Objecten &

datastructuren● Foutafhandeling● Grenzen

● Unit tests● Klassen● Systemen● “Emergent design”● Iteratieve verfijning● Refactoring case● Indicatoren van

slechte code

12

Wat is “heldere” code?

13

Doel:

● Disseminatie● Discussie● Feedback

PS. Koop de boeken! (vakgroepbib?)

14

Conventies

● Kleurtjes:● “good practice”● “bad practice”● nadruk

● Verwijzingen:● CC5 = Clean Code, hoofdstuk 5● EJ5 = Effective Java, hoofdstuk 5● EJ#5 = Effective Java, item 5

15

Onderwerpen

Klassen

Functies

Unit tests

Ontwerp

Stijl

16

Klassen

17

Klassen

● EJ4 “Classes and Interfaces”● CC6 “Objects and Data Structures”● CC10 “Classes”

18

Richtlijnen voor klassen (CC10)

● Klassen moeten klein zijn● Geen “God-klassen” (vb. DomainContoller!)● “Single Responsibility Principle”: er is maar 1 reden

om de klasse te wijzigen● Cohesie: klein aantal instantievariabelen, methods

manipuleren meerdere instantievariabelen

19

Beperk “mutability” (EJ#15)

● Geen mutators● Laat geen overerving toe● Alle velden final● Alle velden private● Geen toegang tot wijzigbare componenten

20

Voorbeeld: Breuken

public class Fraction {

public int numerator; public int denominator; }

21

Breuken: “Bean” patternpublic class Fraction { private int numerator; private int denominator; public Fraction(int numerator, int denominator) { this.numerator = numerator; this.denominator = denominator; reduce(); }

public int getNumerator() { return numerator; } public int getDenominator() { return denominator; } public void setNumerator(int numerator) { this.numerator = numerator; reduce(); } public void setDenominator(int denominator) { this.denominator = denominator; reduce(); } ...}

22

Breuken: hulpcodeprivate void reduce() { int gcd = gcd(numerator, denominator); numerator /= gcd; denominator /= gcd);}

private static int gcd(int a, int b) { if (b==0) return a; return gcd(b,a%b);}

@Override public String toString() { return "" + getNumerator() + "/" + getDenominator();}

23

Breuken: rekenen

public void add(Fraction that) { numerator = this.getNumerator() * that.getDenominator() + this.getDenominator() * that.getNumerator(); denominator = this.getDenominator() * that.getDenominator(); reduce();} public void multiply(Fraction that) { numerator = this.getNumerator() * that.getNumerator(); denominator = this.getDenominator() * that.getDenominator(); reduce();}

24

Onveranderlijke Breuken

public final class Fraction {

private final int numerator; private final int denominator;

public Fraction(int numerator, int denominator) { this.numerator = numerator; this.denominator = denominator; }

public int getNumerator() { return numerator; } public int getDenominator() { return denominator; }}

25

Of misschien zelfs

public final class Fraction {

public final int numerator; public final int denominator;

public Fraction(int numerator, int denominator) { this.numerator = numerator; this.denominator = denominator; }}

26

Terzijde: hetzelfde in Scala ;-)

class Fraction(val numerator: Int, val denominator: Int)

27

Rekenen met onveranderlijke breuken

public Fraction add(Fraction that) { return new Fraction( this.numerator * that.denominator + that.numerator * this.denominator, this.denominator * that.denominator);}

public Fraction multiply(Fraction that) { return new Fraction( this.numerator * that.numerator, this.denominator * that.denominator);}

28

Wijzigbare componentenpublic class Farm { private Field[][] fields; public Farm(int width) { this.fields = new Field[width][width]; initFields(); } private void initFields() { … } public Field[][] getFields() { return fields; }}

Farm farm = new Farm(4);Field[][] fields = farm.getFields();fields[2][3] = null; // zou niet mogen!

29

Wijzigbare componenten

● Geef individuele elementen terug, vb. public Field getField(int row, int col) { return fields[row][col]; }

● Maak een “defensieve kopie”

30

Let op! (EJ#39)public final class Period { private final Date start; private final Date end; public Period(Date start, Date end) { this.start = start; this.end = end; }

public Date getStart() { return start; } public Date getEnd() { return end; }}

31

Aanval op interne toestand Period

Date start = new Date(2012, 06, 01);Date end = new Date(2012, 06, 30);Period p = new Period(start, end);

end.setYear(2013);

// Deze test zal falen!assertEquals(2012, p.getEnd().getYear());

32

Defensieve kopie

public Period(Date start, Date end) { this.start = new Date(start.getTime()); this.end = new Date(end.getTime());}

33

Immutability – voordelen

● Simpel: één toestand● Makkelijker testen● Altijd thread-safe!● Kan je hergebruiken

public static final Fraction ZERO = new Fraction(0,0);public static final Fraction ONE = new Fraction(1,1);

● Kopies maken eigenlijk overbodig● Bouwstenen voor andere objecten

34

Immutability – nadelen

● Veel objecten aanmaken● op te lossen, bv. met static factories (zie verder)

35

Is doorgedreven “immutability”mogelijk / wenselijk?

36

Verkies compositie boven overerving (EJ#16)

● Overerving kan● binnen zelfde package, onder controle van zelfde

programmeurs● van specifiek daarvoor ontworpen klassen● van interfaces

● Overerving vermijden● van “gewone” concrete klassen over packages heen

37

Waarom?

● Overerving breekt encapsulatie● Subklassen hangen af van implementatie superklasse

● Superklasse wijzigen problemen in subklassen● Compilatie● Verkeerd gedrag● Beveiligingsproblemen

● Oplossing: “wrapper class”

38

Verkies interfaces boven abstracte klassen (EJ#18)

● Bestaande klassen kunnen makkelijk aangepast worden om nieuwe interface te implementeren

● Interfaces zijn ideaal voor het definiëren van “mixins” (vgl. Scala Traits, Ruby Modules)

● Interfaces maken niet-hierarchische typeframeworks mogelijk

● Veilige manier om functionaliteit uit te breiden

39

Niet-hierarchische typeframeworks

40

Nadelen

● Geen implementatie● voorzie basisimplementatie (“skeletal”)● kan jouw klasse niet overerven van basisimpl.?

“simulated multiple inheritance”

● Eens een interface gepubliceerd is, kan je niet meer wijzigen

41

Simulated Multiple Inheritance

42

Objecten vs Datastructuren (CC6)

● Objecten● Verbergen data/implementatie achter abstracties● Hebben functies om deze data te bewerken

● Datastructuren● Hebben data● Hebben geen functies van belang

43

Vb: 2 implementaties voor vormen

● Datastructuren/procedureel

public class Square { public Point topLeft; public double side;}public class Circle { public Point center; public double radius;}

44

public class Geometry { public double area(Object shape) { if (shape instanceof Square) { Square s = (Square)shape; return s.side * s.side; } else if(shape instanceof Circle) { Circle c = (Circle) shape; return c.radius * c.radius * Math.PI; } else { throw new IllegalArgumentException( "Not a known shape"); } }}

Jamaar, da's geen OO!

45

Vb: 2 implementaties voor vormen

● Objectgeorienteerd

public interface Shape { public double area();}

public class Square implements Shape { private Point topLeft; private double side; @Override public double area() { return side * side; }}

46

public class Circle implements Shape { private Point center; private double radius; @Override public double area() { return Math.PI * radius * radius; }}

47

Twee soorten refactorings

● Functie toevoegen (bv. perimeter())● Procedureel: enkel Geometry aanpassen

● Shapes en hun “clients” blijven ongewijzigd!● OO: ALLE Shapes aanpassen

● Shape toevoegen (bv. Rectangle)● Procedureel: ALLE functies in Geometry aanpassen● OO: enkel Rectangle-klasse schrijven

48

Is de “procedurele” aanpak uit het vorige voorbeeld soms toelaatbaar/aangewezen?

49

Functies

50

Functies / methods

● CC3 Functions● EJ2 Creating and destroying objects● EJ3 Methods common to all objects● EJ5 Methods

51

Functies mogen maar één ding doenZe moeten dat goed doenZe mogen alleen dat doen

52

Richtlijnen voor functies (CC3)

● Kort! => verstaanbaar● geen geneste controlestructuren● ingewikkelde tests in aparte functie

● Eén niveau van abstractie per functie● Beschrijvende namen

● voor functies en variabelen/parameters

● Leesbaar van boven naar beneden● beginnen met “hoofdfunctie”, daarna hulpfuncties

53

Richtlijnen voor functies (CC3)

● Géén neveneffecten● zwakkere betekenis: één ding doen

public boolean checkPwd(String user, String passwd) { … if(hash.equals(storedHash)) { session.initialize(); return true; }}

● sterkere betekenis: geen data muteren● = basisgedachte functioneel programmeren

● N.B. System.out.println() is een neveneffect

54

Richtlijnen voor functies (CC3)

● “Command/query separation”● ofwel iets doen, ofwel een antwoord geven● niet beide

● Géén “output arguments”● vb. Arrays.fill(boolean[] a, boolean val)

● Exceptions ipv “foutcodes” of null (zie ook EJ#43)

● Don't repeat yourself

55

Functie-argumenten (CC3)

● Aantal:● 0 argumenten is best● 1 argument (monad) is het op één na beste● 2 argumenten (dyad) is al moeilijker te begrijpen● 3 argumenten (triad) is ongeveer het maximum

toelaatbare aantal

● Zie ook EJ#40

56

Functie-argumenten (CC3)

● Geen vlag-argumenten● = booleans die gedrag veranderen● Schrijf 2 functies!

● Lange argumentenlijsten● Gebruik argument-objecten

Circle makeCircle(double x, double y, double radius)Circle makeCircle(Point center, double radius)

● Varargs tellen als één argumentvoid monad(Integer... args)void dyad(String name, Integer... args)

57

Creëren van objecten (EJ2)

● Static factory methods ipv constructors (EJ#1)public static Fraction valueOf(int numerator, int denominator) {

int g = gcd(numerator, denominator); return new Fraction(numerator / g, denominator / g);}

private Fraction(int numerator, int denominator) { this.numerator = numerator; this.denominator = denominator;}

58

Voordelen van static factory methods

● Hebben naam, returntype● Creëren niet noodzakelijk een nieuw object

● caching● “instance-controlled” klasse, bv. Boolean● laat toe om te garanderen dat bij immutable klassen

geldt: a.equals(b) als en slechts als a == b

● Kunnen object van subtype teruggeven

59

Nadelen van static factory methods

● Onmogelijk overerven van klassen zonder publieke/protected constructors

● misschien niet echt een nadeel

● niet te onderscheiden van andere static methods● naamgeving: valueOf(), of(), newInstance(),

getInstance()

60

Creëren van objecten (EJ2)

● Builder pattern: voor constructors met● teveel parameters● optionele/default parameters● verschillende parameters van zelfde type

61

Voorbeeld: Boerderij-object aanmaken

public Farm(String farmName, String playerName, double startBudget, int gridWidth) {

this.farmName = farmName; this.playerName = playerName; this.startBudget = startBudget; this.width = width; this.fields = makeEmptyFields();}

62

Builder (binnen Farm)public static class Builder { String farmName = ""; String playerName = ""; double startBudget = 1_000.0; int gridWidth = 4; public Builder withFarmName(String farmName) { this.farmName = farmName; return this; } public Builder withPlayerName(String playerName) { this.playerName = playerName; return this; } public Builder withStartBudget(double budget) { this.startBudget = budget; return this; } public Builder withGridWidth(int gridWidth) { this.gridWidth = gridWidth; return this; } public Farm build() { return new Farm(this); }}

63

Constructorsprivate Farm(String farmName, String playerName, double startBudget, int gridWidth) { this.farmName = farmName; this.playerName = playerName; this.budget = startBudget; this.gridWidth = gridWidth; this.fields = makeEmptyFields();}

private Farm(Builder builder) { this(builder.farmName, builder.playerName, builder.startBudget, builder.gridWidth);}

64

Client code

Farm farm = new Farm.Builder() .withFarmName("Carlokes") .withPlayerName("René") .withStartBudget(1_000.00) .withGridWidth(5) .build();

65

Voordelen van Builders

● Autocomplete helpt bij invullen van parameters● Niet meer onthouden in welke volgorde

parameters komen● Vermijden verschillende ctors voor default-

waarden● Opleggen van invariants bij objectcreatie● Verschillende varargs mogelijk● Makkelijker parameters toevoegen

66

Let op bij implementeren equals() (EJ#8)

● Enkel implementeren wanneer nodig● Typisch voor “waarde-objecten,” bv. Date,

Integer, Fraction● Respecteer het “contract” van equals()

67

equals() is een equivalentierelatie

● Reflexief: x ≠ null: x.equals(x)

● Symmetrisch: x,y ≠ null: x.equals(y) y.equals(x)

● Transitief: x,y,z ≠ null: x.equals(y) en y.equals(z) x.equals(z)

● Consistent: x,y ≠ null: x.equals(y) geeft telkens zelfde waarde terug

● x ≠ null: x.equals(null) geeft altijd false terug

68

Recept voor equals(Object o)

1.Controleer of o referentie naar dit object is● zo ja true

2.Controleer met instanceof of o het correcte type heeft,

● zo niet false● zo ja, casten naar juiste type

3.Controleer of elk “significant” attribuut van o overeenkomt met het corresponderende attribuut van dit object

69

vb. Fraction(gegenereerd door Eclipse!)

@Override public boolean equals(Object obj) { if (this == obj) // 1 return true; if (obj == null) return false; if (getClass() != obj.getClass()) // 2 return false; Fraction other = (Fraction) obj; if (denominator != other.denominator) // 3 return false; if (numerator != other.numerator) return false; return true; }

70

Overschrijf hashCode() als je equals() overschrijft (EJ#9)

● Zoniet overtreed je contract van Object.hashCode()

● vb. x,y ≠ null: x.equals(y) x.hashCode() == y.hashCode()

● Klasse zal niet werken in HashMap, HashSet, Hashtable

● default impl.: verschillend object verschillende hashCodes

71

Recept voor hashCode()

● cfr. boek● gebruik door Eclipse gegenereerde hashCode()

72

vb. Fraction

@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + denominator; result = prime * result + numerator; return result; }

73

hashCode voor ingewikkeld immutable object

● “Lazily initialized, cached hashCode”

private volatile int hashCode;

@Override public int hashCode() { if(hashCode == 0) { final int prime = 31; int result = 1; result = prime * result + denominator; result = prime * result + numerator; hashCode = result; } return hashCode;}

74

Altijd toString() overschrijven (EJ#10)

● Klasse makkelijker te gebruikenvb. Fraction: “3/5” ipv “Fraction@163b94”

● Bevat zo mogelijk alle interessante info uit object● Documenteer formaat in javadoc● Alle info in de string is via accessors/publieke

velden te verkrijgen

75

hashCode(), equals() en toString() in Scala ;-)

case class Fraction( val numerator: Int, val denominator: Int)

76

Argumenten controleren (EJ#38)

● “Client-code is de vijand”● Expliciet maken van veronderstellingen over

gebruik van de method● Vermijden van problemen bij geven van

verkeerde/onverwachte input● Sneller fouten opsporen

77

Argumenten controleren (EJ#38)

● Publieke methods:● Gebruik IllegalArgumentException en duidelijke

foutboodschap● Documenteer met Javadoc @throws

● Niet-publieke methods● Gebruik assert● Wordt enkel gecompileerd met optie -ea

78

Schrijf nooit “return null;”

● Client-code verplicht uitzondering te behandelen● “null-checks” vervuilen je code

● Aanleiding tot NullPointerException● Alternatief:

● Exception● “Leeg” object, bv. Collections.emptyList()● Opl. in Scala: Option[T] → Some[T] of None

79

Exceptions

● EJ#60: bij voorkeur standaard-exceptions gebruiken

● EJ#62: alle mogelijke exceptions documenteren met @throws

● EJ#65: niet onder de mat vegen● try { … }catch(SomeException e) {}

● try { … }catch(SomeException e) { e.printStackTrace(); }

80

Checked vs Unchecked Exceptions

● Tegenspraak tussen EJ en CC● EJ#58: Checked exceptions voor uitzonderlijke

condities, runtime exceptions voor bugs● daarvoor zijn ze ontworpen!

● CC7: “The debate is over. Use Unchecked Exceptions.”

● “doorbreken encapsulatie”● ontbreken van checked exceptions staan robuuste

code niet in de weg

81

Checked vs Unchecked Exceptions

● EJ#59. Onnodig gebruik van checked exceptions vermijden

● “lastig” voor gebruiker API● nodigt uit slechte foutafhandeling te schrijven

● EJ#64. Streven naar “atomair falen”● Exception laat object in toestand van vóór method →

call● cfr. ACID bij databases

82

Unit tests

83

3 wetten van Test Driven Development (CC9)

1. Schrijf geen productiecode vóór een mislukkende unit test

2. Schrijf niet meer in een unit test dan voldoende om te falen (niet compileren = falen)

3. Schrijf niet meer productiecode dan voldoende om de falende test te laten slagen

84

Aanbevelingen voor Unit tests

● Hou de test-code “clean”, leesbaar

● testcode is even belangrijk als productiecode

● Domeinspecifieke test-taal

● = “utility methods” die testcode leesbaarder maken

● Eén assert per test

● Niet in steen gebeiteld, maar hou minimaal● → Eén concept per test

● Gebruik code coverage tool & streef naar 100%

● Private method package local maken om te kunnen testen mag!

85

F.I.R.S.T. principe voor Unit Tests

● Fast: je moet tests vaak willen draaien● Independent: “waterval” van problemen

vermijden● Repeatable: in ontwikkelings/QA/UA/productie-

omgevingen● Self-Validating: “wit-zwart”, geen “grijs”● Timely: tijdig schrijven zorgt voor testbare code

86

Ontwerpen

87

“Emergent design” (CC12)

● Een ontwerp is “eenvoudig” als het volgende regels volgt:

● draait alle tests● bevat geen duplicatie● is expressief, drukt de bedoeling van de

programmeur uit● minimaliseert het aantal klassen en functies

● < Kent Beck, “Extreme Programming Explained”

88

“Emergent design”

● Met deze regels “ontstaat” een goed ontwerp “als vanzelf” tijdens het programmeren

● Maakt het makkelijker bv. “Single Responsibility Principle” of “Dependency Inversion Principle” te volgen

89

Alle tests draaien

● Een goed ontwerp produceert een systeem dat zich gedraagt zoals bedoeld was

● Zorgen voor testbare code zorgt voor beter ontwerp

● leidt tot “high cohesion – low coupling”

● Code opkuisen zal functionaliteit niet breken

90

Geen duplicatie

● Makkelijkst: identieke lijnen code● Ook bvb. int size() vs boolean isEmpty()

● met aparte implementatie voor beide

● “Template methods” gebruiken● Wat met identieke implementatie, maar

verschillende intentie?

91

Intentie vs implementatie(RubySlim voorbeeld)

def slim_to_ruby_method(method_name) value = method_name[0..0].downcase + method_name[1..-1] value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" }end

def to_file_name(module_name) value = module_name[0..0].downcase + module_name[1..-1] value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" }end

● “Slim” methodnaam naar Ruby methodnaam:

● “Slim” packagenaam omzetten naar bestandsnaam

http://www.informit.com/articles/article.aspx?p=1313447

92

Intentie vs implementatie

● Naar elkaar laten verwijzen?● Nee: to_file_name moet niets weten van

methodnamen en v.v.

● Hernoemen naar to_camel_case? ● Nee: client-code moet niets weten van

implementatiedetails

● Aparte method to_camel_case + oorspronkelijke 2 er naar laten verwijzen

● = toepassing één niveau van abstractie

93

Intentie vs implementatie

def slim_to_ruby_method(method_name) camel_to_underscore(method_name)end

def to_file_name(module_name) camel_to_underscore(module_name)end

def camel_to_underscore(camel_namme) value = camel_name[0..0].downcase + camel_name[1..-1] value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" }end

94

Expressiviteit

● Maak systeem makkelijk begrijpbaar● code drukt uit wat de programmeur bedoelt

● Goede naamgeving● weergave van verantwoordelijkheden● gestandaardiseerde naamen (bv. patterns)

● Goed geschreven Unit Test● = documentatie a.h.v. voorbeeld

95

Expressiviteit

● Voldoende aandacht besteden hieraan● Niet verder doen met iets anders zodra het “werkt”

● Fierheid over je vakmanschap

96

Minimaal aantal klassen en methods

● Tegenspraak met “kleine klassen”?● kan te ver gedreven worden● mag geen “dogma” zijn (vb. scheiden van data- &

gedrag-klassen)● evenwicht

● Tests, elimineren duplicatie, expressiviteit zijn belangrijker

97

Stijl

98

Commentaar (CC4)

● Commentaar is geen oplossing voor slechte code● Druk je intentie uit in code

99

Goede commentaar

● Wettelijke bepalingen (vb. licentie, copyrigth)● Informatieve commentaar

● functienaam zegt het al!● bv. wél uitleg bij ingewikkelde regexp

// Returns the numerator of this Fraction.public int getNumerator() { return numerator;}

100

Goede commentaar

● Intentie uitleggen● Verduidelijking

● vb. betekenis argument/return-waarde● kan best op andere manier in je eigen code● bij API-calls geen keuze

● Waarschuwing consequenties● bv. test die lang duurt

101

Goede commentaar

● TODO● worden bijgehouden in Eclipse, Netbeans

● Javadoc publieke API (cfr. EJ#44)● Let op, Javadocs kunnen even misleidend zijn dan

andere (slechte) commentaar● Is dit goede commentaar?

/** Returns the denominator of this Fraction. * @return the denominator of this Fraction. */public int getDenominator() { return denominator; }

102

Slechte commentaar

● “Gebrabbel”● Redundante commentaar

● zegt hetzelfde als de code (maar dan minder precies)● legt niets uit over intentie code

● Misleidende commentaar● te vaag om te kloppen

● Verplichte commentaar● vb. Javadoc van triviale methods

103

Slechte commentaar

● “Log” van wijzigingen● is werk voor versiebeheersysteem!

● Commentaar als vervanging van goede variabele-/methodnaam

● Positiemarkeringen//---------- Accessors --------------------------

● Commentaar bij sluiten accolade

104

Slechte commentaar

● Vermeldingen auteurs● Hoort in versiebeheersysteem

● Code in commentaar● Vervuilt de code● Wat is de intentie? Waarom in commentaar?● Versiebeheer!

● HTML commentaar

105

Slechte commentaar

● Niet-lokale informatie● Teveel informatie● Onduidelijke link met code● Functie-headers● Javadocs in niet-publieke code

106

Naamgeving (CC2)

● Namen geven intentie bloot● int d; // elapsed time in days

● Vermijd desinformatie● private Person[] personList;● kleine L (l of 1?), hoofletter O (O of 0?)

● Namen zijn uitspreekbaar

107

Naamgeving

● Zinvol onderscheid tussen namen● variabele niet verkeerd spellen om onderscheid te

maken met andere, vb. class klass↔● geen getalseries, vb. a1, a2, a3, …● redundante namen, vb. denominatorVariable

● Namen zijn zoekbaar● hoe breder de scope, hoe langer de naam● variabelen met 1 letter enkel lokaal in korte methods

108

Naamgeving

● Vermijd “coderingen”● “Hongaarse notatie” met type in de naam, vb.

phoneString● “Member prefix” voor onderscheid met functie-

argumenten, vb. private int mNumerator;● Interface prefix, vb. IShapeFactory

● Probeer niet grappig te zijn

109

Naamgeving

● Consistent: één woord per concept● fetch retrieve get↔ ↔● Controller Manager Driver↔ ↔

● Namen uit oplossingsdomein (vakjargon, patterns, wiskundige termen, …)

● Namen uit probleemdomein

110

Naamgeving

● Klassenamen● gebaseerd op zelfstandige naamwoorden, vb.

Customer, WikiPage, AddressParser● vermijd te algemene woorden als Manager,

Processor, Data, Info, Controller● geen werkwoord

111

Naamgeving

● Methodnamen● gebaseerd op werkwoorden● accessors/mutators beginnen met get/set● predicaten beginnen met is● constructor overloading factory methods die →

argument beschrijven● vb. Complex(double) →Complex.fromRealNumber(double)

112

En verder...

113

Waarom de boeken nog lezen/kopen?

● Verschillende topics niet aan bod gekomen● Concurrency (CC13 & appendix A, EJ10)● Praktijkvoorbeelden refactoring (CC14, CC16)● “Smells and Heuristics” (CC17)

114

Bedankt!

Recommended