SlideShare a Scribd company logo
1 of 114
Download to read offline
Clean, effective Java
    Bert Van Vreckem
2
?
    3
(TODO)

         4
5
6
Josh Bloch
             7
Inhoud EJ
                       (2nd Ed, 2008)

●   Creatie objecten           ●   Algemene
●   Gemeensch. methods             programmeertips
●   Klassen en interfaces
                               ●   Foutafhandeling
●   Generieke klassen
                               ●   Concurrency
●   Enums, annotaties
                               ●   Serialisatie
●   Methods
                               ●   78 “items” (EJ#nn)

                                                        8
9
Robert 'Uncle Bob' Martin

                            10
Inhoud CC
                        (2009)

●   Naamgeving              ●    Unit tests
●   Functies                ●    Klassen
●   Commentaar              ●    Systemen
●   Formattering            ●    “Emergent design”
●   Objecten &              ●    Iteratieve verfijning
    datastructuren          ●    Refactoring case
●   Foutafhandeling         ●    Indicatoren van
●   Grenzen                      slechte code            11
Wat is “heldere” code?




                         12
Doel:
●   Disseminatie
●   Discussie
●   Feedback

PS. Koop de boeken! (vakgroepbib?)




                                     13
Conventies
●   Kleurtjes:
    ●   “good practice”
    ●   “bad practice”
    ●   nadruk
●   Verwijzingen:
    ●   CC5 = Clean Code, hoofdstuk 5
    ●   EJ5 = Effective Java, hoofdstuk 5
    ●   EJ#5 = Effective Java, item 5
                                            14
Onderwerpen
   Klassen
  Functies
  Unit tests
  Ontwerp
     Stijl
               15
Klassen


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




                                        17
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




                                                                18
Beperk “mutability” (EJ#15)
●   Geen mutators
●   Laat geen overerving toe
●   Alle velden final
●   Alle velden private
●   Geen toegang tot wijzigbare componenten



                                              19
Voorbeeld: Breuken

public class Fraction {

    public int numerator;
    public int denominator;

}




                              20
Breuken: “Bean” pattern
public 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(); }
    ...                                                 21
}
Breuken: hulpcode
private 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();
}


                                                           22
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();
}




                                                               23
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; }
}


                                                          24
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;
    }
}




                                                        25
Terzijde: hetzelfde in Scala ;-)



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




                                                           26
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);
}


                                                    27
Wijzigbare componenten
public 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!
                                                 28
Wijzigbare componenten
●   Geef individuele elementen terug, vb.
     public Field getField(int row, int col) {
         return fields[row][col];
     }

●   Maak een “defensieve kopie”




                                                 29
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; }
}




                                               30
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());




                                               31
Defensieve kopie



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




                                              32
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
                                                             33
Immutability – nadelen
●   Veel objecten aanmaken
    ●   op te lossen, bv. met static factories (zie verder)




                                                              34
Is doorgedreven “immutability”
     mogelijk / wenselijk?




                                 35
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


                                                           36
Waarom?
●   Overerving breekt encapsulatie
    ●   Subklassen hangen af van implementatie superklasse
●   Superklasse wijzigen  problemen in subklassen
    ●   Compilatie
    ●   Verkeerd gedrag
    ●   Beveiligingsproblemen
●   Oplossing: “wrapper class”

                                                             37
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

                                                       38
Niet-hierarchische typeframeworks




                                    39
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



                                                         40
Simulated Multiple Inheritance




                                 41
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



                                                          42
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;
              }




                                          43
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!                      44
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;
            }
        }
                                                 45
public class Circle implements Shape {
    private Point center;
    private double radius;

    @Override public double area() {
        return Math.PI * radius * radius;
    }
}




                                            46
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


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




                                                      48
Functies


           49
Functies / methods
●   CC3 Functions
●   EJ2 Creating and destroying objects
●   EJ3 Methods common to all objects
●   EJ5 Methods




                                          50
Functies mogen maar één ding doen
    Ze moeten dat goed doen
    Ze mogen alleen dat doen




                                    51
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
                                                           52
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
                                                                53
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
                                                      54
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


                                                            55
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 argument
        void monad(Integer... args)
        void dyad(String name, Integer... args)

                                                               56
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;
}


                                                        57
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

                                                              58
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()




                                                       59
Creëren van objecten (EJ2)
●   Builder pattern: voor constructors met
    ●   teveel parameters
    ●   optionele/default parameters
    ●   verschillende parameters van zelfde type




                                                   60
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();
 }



                                        61
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);
    }
}                                                             62
Constructors
private 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);
}                                      63
Client code


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




                                 64
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
                                                     65
Let op bij implementeren equals()
                  (EJ#8)
●   Enkel implementeren wanneer nodig
●   Typisch voor “waarde-objecten,” bv. Date,
    Integer, Fraction
●   Respecteer het “contract” van equals()




                                                66
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
                                                          67
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                          68
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;
}
                                                 69
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

                                                             70
Recept voor hashCode()
●   cfr. boek
●   gebruik door Eclipse gegenereerde hashCode()




                                                   71
vb. Fraction


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




                                             72
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;
      }
                                                       73
Altijd toString() overschrijven (EJ#10)
●   Klasse makkelijker te gebruiken
    vb. 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



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


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




                                        75
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



                                                  76
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


                                                         77
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

                                                       78
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(); }                    79
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
                                                          80
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

                                                              81
Unit tests


             82
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



                                                      83
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!                                                      84
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

                                                        85
Ontwerpen


            86
“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”

                                                       87
“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




                                                        88
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



                                                    89
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?


                                               90
Intentie vs implementatie
                   (RubySlim voorbeeld)
●   “Slim” methodnaam naar Ruby methodnaam:
    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
●   “Slim” packagenaam omzetten naar
    bestandsnaam
    def to_file_name(module_name)
      value = module_name[0..0].downcase + module_name[1..-1]
      value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" }
    end
           http://www.informit.com/articles/article.aspx?p=1313447   91
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
                                                    92
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



                                                          93
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

                                                    94
Expressiviteit
●   Voldoende aandacht besteden hieraan
    ●   Niet verder doen met iets anders zodra het “werkt”
●   Fierheid over je vakmanschap




                                                             95
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


                                                           96
Stijl


        97
Commentaar (CC4)
●   Commentaar is geen oplossing voor slechte code
●   Druk je intentie uit in code




                                                     98
Goede commentaar
●   Wettelijke bepalingen (vb. licentie, copyrigth)
●   Informatieve commentaar
    // Returns the numerator of this Fraction.
    public int getNumerator() {
        return numerator;
    }
    ●   functienaam zegt het al!
    ●   bv. wél uitleg bij ingewikkelde regexp


                                                      99
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

                                                     100
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; }

                                                               101
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
                                                               102
Slechte commentaar
●   “Log” van wijzigingen
    ●   is werk voor versiebeheersysteem!
●   Commentaar als vervanging van goede
    variabele-/methodnaam
●   Positiemarkeringen
    //---------- Accessors --------------------------

●   Commentaar bij sluiten accolade


                                                        103
Slechte commentaar
●   Vermeldingen auteurs
    ●   Hoort in versiebeheersysteem
●   Code in commentaar
    ●   Vervuilt de code
    ●   Wat is de intentie? Waarom in commentaar?
    ●   Versiebeheer!
●   HTML commentaar

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



                                     105
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



                                                     106
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
                                                                107
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


                                                        108
Naamgeving
●   Consistent: één woord per concept
    ●   fetch ↔ retrieve ↔ get
    ●   Controller ↔ Manager ↔ Driver
●   Namen uit oplossingsdomein (vakjargon,
    patterns, wiskundige termen, …)
●   Namen uit probleemdomein



                                             109
Naamgeving
●   Klassenamen
    ●   gebaseerd op zelfstandige naamwoorden, vb.
        Customer, WikiPage, AddressParser
    ●   vermijd te algemene woorden als Manager,
        Processor, Data, Info, Controller
    ●   geen werkwoord




                                                     110
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)



                                                        111
En verder...


               112
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)




                                                       113
Bedankt!


           114

More Related Content

Featured

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by HubspotMarius Sescu
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTExpeed Software
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Clean, effective java

  • 1. Clean, effective Java Bert Van Vreckem
  • 2. 2
  • 3. ? 3
  • 4. (TODO) 4
  • 5. 5
  • 6. 6
  • 8. Inhoud EJ (2nd Ed, 2008) ● Creatie objecten ● Algemene ● Gemeensch. methods programmeertips ● Klassen en interfaces ● Foutafhandeling ● Generieke klassen ● Concurrency ● Enums, annotaties ● Serialisatie ● Methods ● 78 “items” (EJ#nn) 8
  • 9. 9
  • 10. Robert 'Uncle Bob' Martin 10
  • 11. Inhoud CC (2009) ● Naamgeving ● Unit tests ● Functies ● Klassen ● Commentaar ● Systemen ● Formattering ● “Emergent design” ● Objecten & ● Iteratieve verfijning datastructuren ● Refactoring case ● Foutafhandeling ● Indicatoren van ● Grenzen slechte code 11
  • 13. Doel: ● Disseminatie ● Discussie ● Feedback PS. Koop de boeken! (vakgroepbib?) 13
  • 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 14
  • 15. Onderwerpen Klassen Functies Unit tests Ontwerp Stijl 15
  • 16. Klassen 16
  • 17. Klassen ● EJ4 “Classes and Interfaces” ● CC6 “Objects and Data Structures” ● CC10 “Classes” 17
  • 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 18
  • 19. Beperk “mutability” (EJ#15) ● Geen mutators ● Laat geen overerving toe ● Alle velden final ● Alle velden private ● Geen toegang tot wijzigbare componenten 19
  • 20. Voorbeeld: Breuken public class Fraction { public int numerator; public int denominator; } 20
  • 21. Breuken: “Bean” pattern public 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(); } ... 21 }
  • 22. Breuken: hulpcode private 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(); } 22
  • 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(); } 23
  • 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; } } 24
  • 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; } } 25
  • 26. Terzijde: hetzelfde in Scala ;-) class Fraction(val numerator: Int, val denominator: Int) 26
  • 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); } 27
  • 28. Wijzigbare componenten public 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! 28
  • 29. Wijzigbare componenten ● Geef individuele elementen terug, vb. public Field getField(int row, int col) { return fields[row][col]; } ● Maak een “defensieve kopie” 29
  • 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; } } 30
  • 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()); 31
  • 32. Defensieve kopie public Period(Date start, Date end) { this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); } 32
  • 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 33
  • 34. Immutability – nadelen ● Veel objecten aanmaken ● op te lossen, bv. met static factories (zie verder) 34
  • 35. Is doorgedreven “immutability” mogelijk / wenselijk? 35
  • 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 36
  • 37. Waarom? ● Overerving breekt encapsulatie ● Subklassen hangen af van implementatie superklasse ● Superklasse wijzigen  problemen in subklassen ● Compilatie ● Verkeerd gedrag ● Beveiligingsproblemen ● Oplossing: “wrapper class” 37
  • 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 38
  • 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 40
  • 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 42
  • 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; } 43
  • 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! 44
  • 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; } } 45
  • 46. public class Circle implements Shape { private Point center; private double radius; @Override public double area() { return Math.PI * radius * radius; } } 46
  • 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 47
  • 48. Is de “procedurele” aanpak uit het vorige voorbeeld soms toelaatbaar/aangewezen? 48
  • 49. Functies 49
  • 50. Functies / methods ● CC3 Functions ● EJ2 Creating and destroying objects ● EJ3 Methods common to all objects ● EJ5 Methods 50
  • 51. Functies mogen maar één ding doen Ze moeten dat goed doen Ze mogen alleen dat doen 51
  • 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 52
  • 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 53
  • 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 54
  • 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 55
  • 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 argument void monad(Integer... args) void dyad(String name, Integer... args) 56
  • 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; } 57
  • 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 58
  • 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() 59
  • 60. Creëren van objecten (EJ2) ● Builder pattern: voor constructors met ● teveel parameters ● optionele/default parameters ● verschillende parameters van zelfde type 60
  • 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(); } 61
  • 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); } } 62
  • 63. Constructors private 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); } 63
  • 64. Client code Farm farm = new Farm.Builder() .withFarmName("Carlokes") .withPlayerName("René") .withStartBudget(1_000.00) .withGridWidth(5) .build(); 64
  • 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 65
  • 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() 66
  • 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 67
  • 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 68
  • 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; } 69
  • 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 70
  • 71. Recept voor hashCode() ● cfr. boek ● gebruik door Eclipse gegenereerde hashCode() 71
  • 72. vb. Fraction @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + denominator; result = prime * result + numerator; return result; } 72
  • 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; } 73
  • 74. Altijd toString() overschrijven (EJ#10) ● Klasse makkelijker te gebruiken vb. 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 74
  • 75. hashCode(), equals() en toString() in Scala ;-) case class Fraction( val numerator: Int, val denominator: Int) 75
  • 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 76
  • 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 77
  • 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 78
  • 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(); } 79
  • 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 80
  • 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 81
  • 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 83
  • 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! 84
  • 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 85
  • 86. Ontwerpen 86
  • 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” 87
  • 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 88
  • 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 89
  • 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? 90
  • 91. Intentie vs implementatie (RubySlim voorbeeld) ● “Slim” methodnaam naar Ruby methodnaam: 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 ● “Slim” packagenaam omzetten naar bestandsnaam def to_file_name(module_name) value = module_name[0..0].downcase + module_name[1..-1] value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" } end http://www.informit.com/articles/article.aspx?p=1313447 91
  • 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 92
  • 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 93
  • 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 94
  • 95. Expressiviteit ● Voldoende aandacht besteden hieraan ● Niet verder doen met iets anders zodra het “werkt” ● Fierheid over je vakmanschap 95
  • 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 96
  • 97. Stijl 97
  • 98. Commentaar (CC4) ● Commentaar is geen oplossing voor slechte code ● Druk je intentie uit in code 98
  • 99. Goede commentaar ● Wettelijke bepalingen (vb. licentie, copyrigth) ● Informatieve commentaar // Returns the numerator of this Fraction. public int getNumerator() { return numerator; } ● functienaam zegt het al! ● bv. wél uitleg bij ingewikkelde regexp 99
  • 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 100
  • 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; } 101
  • 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 102
  • 103. Slechte commentaar ● “Log” van wijzigingen ● is werk voor versiebeheersysteem! ● Commentaar als vervanging van goede variabele-/methodnaam ● Positiemarkeringen //---------- Accessors -------------------------- ● Commentaar bij sluiten accolade 103
  • 104. Slechte commentaar ● Vermeldingen auteurs ● Hoort in versiebeheersysteem ● Code in commentaar ● Vervuilt de code ● Wat is de intentie? Waarom in commentaar? ● Versiebeheer! ● HTML commentaar 104
  • 105. Slechte commentaar ● Niet-lokale informatie ● Teveel informatie ● Onduidelijke link met code ● Functie-headers ● Javadocs in niet-publieke code 105
  • 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 106
  • 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 107
  • 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 108
  • 109. Naamgeving ● Consistent: één woord per concept ● fetch ↔ retrieve ↔ get ● Controller ↔ Manager ↔ Driver ● Namen uit oplossingsdomein (vakjargon, patterns, wiskundige termen, …) ● Namen uit probleemdomein 109
  • 110. Naamgeving ● Klassenamen ● gebaseerd op zelfstandige naamwoorden, vb. Customer, WikiPage, AddressParser ● vermijd te algemene woorden als Manager, Processor, Data, Info, Controller ● geen werkwoord 110
  • 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) 111
  • 112. En verder... 112
  • 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) 113
  • 114. Bedankt! 114