De presentatie wordt gedownload. Even geduld aub

De presentatie wordt gedownload. Even geduld aub

Introductie Verschillende refactoringstappen Refactoring.

Verwante presentaties


Presentatie over: "Introductie Verschillende refactoringstappen Refactoring."— Transcript van de presentatie:

1 Introductie Verschillende refactoringstappen Refactoring

2 2 Fowler, 2000. Met bijdrage van o.a. Erich Gamma (GOF) en Kent Beck (JUnit) Behoort tot de basisvocab van sw. ontwikkelaars.

3 Suboptimaal software Complexiteit en flexibiliteit van de componenten van de software niet optimaal. Leidt tot hoger onderhoud (bug fixing, feature aanpassen, feature toevoegen) kosten Onderschat onderhoud niet  vaak grote kostenpost! 3

4 Refactoring Dus bijvoorbeeld performance is niet de hoofddoel van refactoring. 4 Refactoring is transformatie op (de code van) een software om de software makkelijker te begrijpen en te onderhouden; de transformatie mag de observeerbaar gedraag van de software niet veranderen.

5 Bad smells in software Indicatoren dat refactoring nodig is: Code duplicatie Lange methode, grote klasse Methode met veel parameters Divergent fix, shotgun fix Feature envy Switch Temporary field (tijdelijk attribuut) Commentaar 5

6 Hoe doen we dat ? Formele theorie van refactoring? Klassen zijn echter complex (attributen, methoden, toegangmodifiers, statics, inheritence …), zulke theorie wordt snel onpraktisch. We doen refactoring als een serie van kleine stappen. Zoals: Geef een betere naam aan een methode Extractie van een block code tot een methode We leunen op testing om te garanderen dat elke stap de software consistent houdt. (retest na elke stap!) Maar deze zijn toch slechts cosmetische modificaties?  niet erg belangrijk? De doel van refactoring is pragmatische  onderhoud Refactoring doen we structureel 6

7 Refactoring doen we structureel… Regelmatig In principe kun je je eigen refactoringstappen bedenken. Fowler’s cataloog (~50)  nuttig als je basis vocabulaire. Je hebt een goede test suite nodig Dekkend Autonoom 7

8 Voorbeeld 8 Film - titel -prijsCode : int +$ KINDER +$ STANDAARD +$ NIEUW Film - titel -prijsCode : int +$ KINDER +$ STANDAARD +$ NIEUW LeenInfo - aantalDagen LeenInfo - aantalDagen * film Klant - naam - KlantPunten + leen(uitleen) + terug(uitleen) + mkFactuur() Klant - naam - KlantPunten + leen(uitleen) + terug(uitleen) + mkFactuur() * leent Hypothetisch nemen we aan dat dit een onderdeel van een groot software getter en setter  impliciet bron: Fowler

9 Flattening associatieklasse 9 Film - titel -prijsCode : int +$ KINDER +$ STANDAARD +$ NIEUW Film - titel -prijsCode : int +$ KINDER +$ STANDAARD +$ NIEUW LeenInfo - aantalDagen LeenInfo - aantalDagen 1 0..1 film Klant - naam - KlantPunten + leen(uitleen) + terug(uitleen) + mkFactuur() Klant - naam - KlantPunten + leen(uitleen) + terug(uitleen) + mkFactuur() * 1 leent Omdat Java geen 1 e klass associatieklasse niet heeft Alleen voor uitleg laat ik de assoc. klasse omgezet naar gewone klassen en relaties leeninfos

10 10 mkFactuur() { double totPrijs = 0 int punten = 0 println(“Factuur voor ” + naam) for (LeenInfo v : leeninfos) { // bereken de huurprijs van v double v_prijs = 0 switch (v.getFilm().getPrijsCode()) { case Film.GEWOON : v_prijs = … ; break case Film.NIEUW : v_prijs = … ; break case Film.KINDER : v_prijs = … ; break } totPrijs += v_prijs // bereken verkregen klantpunten uit v punten++ if (v.getFilm().getPrijsCode()==Film.NIEUW) punten++ println(“Film ” + v.getNaam() + “, prijs ” + v_prijs) } println(“Tot prijs ” + totprijs) println(“Punten verdiend ” + punten) }

11 11 mkFactuur() { double totPrijs = 0 int punten = 0 println(“Factuur voor ” + naam) for (LeenInfo v : leeninfos) { // bereken de huurprijs van v double v_prijs = 0 switch (v.getFilm().getPrijsCode()) { case Film.GEWOON : v_prijs = … ; break case Film.NIEUW : v_prijs = … ; break case Film.KINDER : v_prijs = … ; break } totPrijs += v_prijs // bereken verkregen klantpunten uit v punten++ if (v.getFilm().getPrijsCode()==Film.NIEUW) punten++ println(“Film ” + v.getNaam() + “, prijs ” + v_prijs) } println(“Tot prijs ” + totprijs) println(“Punten verdiend ” + punten) } “extract method” : promoveer een groep statement tot een methode commentaar geeft hint neem de context ook mee! “extract method” : promoveer een groep statement tot een methode commentaar geeft hint neem de context ook mee!

12 12 mkFactuur() { double totPrijs = 0 int punten = 0 println(“Factuur voor ” + naam) for (LeenInfo v : leeninfos) { double v_prijs = berekenPrijs(v) totPrijs += v_prijs punten += berekenPunten(v) println(“Film ” + v.getNaam() + “, prijs ” + v_prijs) } println(“Tot prijs ” + totprijs) println(“Punten verdiend ” + punten) } Is dit nu makkelijker om te begrijpen of niet ?? Discussiepunt: de tijdelijke lokale variabele

13 13 mkFactuur() { double totPrijs = 0 int punten = 0 println(“Factuur voor ” + naam) for (LeenInfo v : leeninfos) { double v_prijs = berekenPrijs(v) totPrijs += v_prijs punten += berekenPunten(v) println(“Film ” + v.getNaam() + “, prijs ” + v_prijs) } println(“Tot prijs ” + totprijs) println(“Punten verdiend ” + punten) } Maar dit gaat toch ten koste van performance… berekenPrijs(v) Fowler’s stelling: 90% van de tijd is je software bezig met 10% van je code. Performance optimalisatie is makkelijker na refactoring. (gebruik profiler!) replace temp with query

14 14 class Klant { … berekenPrijs(LeenInfo v) { double v_prijs = 0 switch (v.getFilm().getPrijsCode()) { case Film.GEWOON : v_prijs = … ; break case Film.NIEUW : v_prijs = … ; break case Film.KINDER : v_prijs = … ; break } return v_prijs } Film feature envy  “move method” Film Switch over ‘type’  vluchtig…  replace type-code with Strategy GewoneFilm NieuweFilm KinderFilm Werkt helaas niet  we willen de prijscode van een film ook dynamisch te kunnen veranderen.

15 Switch 15 switch (expressie) { case 0 : doe iets ; break case 1 : doe iets ; break case 9 : doe iets ; }

16 16 PrijsStrategie prijs(n) PrijsStrategie prijs(n) GewonePrijs NieuwePrijs KinderPrijs Film berekenPrijs(v) Film berekenPrijs(v) 1 Klant mkFactuur () Klant mkFactuur () LeenInfo aantalDagen LeenInfo aantalDagen * 1 mkFactuur() { double totPrijs = 0 int punten = 0 println(“Factuur voor ” + naam) for (LeenInfo v : leeninfos) { totPrijs += berekenPrijs(v) punten += berekenPunten(v) println(“Film ” + v.getNaam() + “, prijs ” + berekenPrijs(v)) } println(“Tot prijs ” + totprijs) println(“Punten verdiend ” + punten) } v.getFilm().berekenPrijs(v) Na “replace type-code with Strategy”

17 17 PrijsStrategie prijs(n) PrijsStrategie prijs(n) GewonePrijs NieuwePrijs KinderPrijs Film berekenPrijs() Film berekenPrijs() 1 Klant mkFactuur () Klant mkFactuur () LeenInfo aantalDagen LeenInfo aantalDagen * 1 prijs prijs(n) { if (n<=2) return 2.0 else 2.0 + (n – 2)* 1.5 } prijs(n) { if (n<=2) return 2.0 else 2.0 + (n – 2)* 1.5 } berekenPrijs(LeenInfo v) { double v_prijs = 0 switch (getPrijsCode()) { case Film.GEWOON : v_prijs = … ; break case Film.NIEUW : v_prijs = … ; break case Film.KINDER : v_prijs = … ; break } return v_prijs } this.prijs.prijs(v.getAantalDagen()) “replace conditional with polymorphism”

18 18 PrijsStrategie prijs(n) PrijsStrategie prijs(n) GewonePrijs NieuwePrijs KinderPrijs Film berekenPrijs() Film berekenPrijs() 1 Klant mkFactuur () Klant mkFactuur () LeenInfo aantalDagen LeenInfo aantalDagen * 1 prijs berekenPrijs(LeenInfo v) { double v_prijs = 0 switch (getPrijsCode()) { case Film.GEWOON : v_prijs = … ; break case Film.NIEUW : v_prijs = … ; break case Film.KINDER : v_prijs = … ; break } return v_prijs } “replace conditional with polymorphism” berekenPrijs(LeenInfo v) { return prijs.prijs(v.getAantalDagen()) } berekenPrijs(LeenInfo v) { return prijs.prijs(v.getAantalDagen()) }

19 Fowler’s refactoringset Stroomlijnen van methodes (9) Verplaatsen van features (8) Organisatie van data (16) Vereenvoudiging van conditionele statements (8) Vereenvoudiging van methodeaanroep (15) Generalisatie (12) 19

20 Stroomlijnen van methode hoofdwapen: extract method inverse: inline method  als de body net zo duidelijk als de methodenaam. elimineren en introduceren van temp als je een blok B tot een methode uittrekt, moet je ook de context van B mee nemen  temp maakt dit lastiger. 20 Fowler’s stelling: lange methodes zijn vaak bronnen van problemen. (Fowler geeft voorkeur aan (veel) korte methodes)

21 Temp inline temp en introduce explaining variable replace temp with query je kunt niet alle temp elimineren  loopteller, accumulatie var. 21 double verkoopprijs = product.getPrijs() * 1.5 return verkoopprijs > 10.0 double verkoopprijs = product.getPrijs() * 1.5 return verkoopprijs > 10.0 return product.getPrijs() * 1.5 > 10.0 expressie is te complex return verkoopprijs() > 10.0

22 Als je parameterlijst te lang is… Introduce parameter object 22 prijs(datum, leeftijd, isLid, actiekaart, isOpenDag) prijs(persoonsProfiel, datumProfiel) persoonsProfiel leeftijd lidmaatschap actiekaart persoonsProfiel leeftijd lidmaatschap actiekaart datumProfiel datum opendag datumProfiel datum opendag

23 Features verplaatsen hoofdwapen: move method/field (heb je al gezien) splitsen en samenvoegen van klassen introduceren en elimineren van delegatie 23 Verdeel het werk (welke klasse doet wat). Een klasse die te veel doet is ook moeilijker om te begrijpen.

24 Splitsen en samenvoegen Extract class: een klasse C doet te veel. Herken een deel van C die je tot een eigen klasse D kan groeperen. De inverse is inline class. 24 Persoon naam leeftijd straat huisnr postcode … printAdres() Persoon naam leeftijd straat huisnr postcode … printAdres() Persoon naam leeftijd Persoon naam leeftijd Adres straat huisnr postcode printadres() Adres straat huisnr postcode printadres() 1 1

25 Delegatie Hide delegate remove middle man 25 Persoon Adres Postcode getStad() Postcode getStad() adres.getPostcode().getStad() Persoon Adres getStad() Adres getStad() Postcode getStad() Postcode getStad() adres. getStad() getStad() { return postcode.getStad() } als Adres te veel delegatie doet ipv echt werk

26 Stroomlijnen van conditionele stmt Hoofdwapen: extract methode op condities decompose conditional, consolidate conditional, Vereenvoudiging op de structuur remove control flag replace conditional with polymorphism (heb je al gezien) replace nested conditional with guard clauses 26 Complexe domeinlogics vertalen zich naar complexe conditionele statements, die vaak moeilijk te begrijpen en dus foutgevoelig is.

27 Extract methode op conditionele stmt Decompose conditional Consolidate conditional expression 27 if (datum.voor(ZOMER_BEGIN) || datum.na(ZOMER_EIND) ) prijs = 0.8 * prijs if (…???.... (datum ) ) prijs = 0.8 * prijs prijs(persoon) { if (isGratis(persoon)) return 0 return normalePrijs(persoon) } prijs(persoon) { if (isGratis(persoon)) return 0 return normalePrijs(persoon) } prijs(persoon) { if (leeftijd ≤ 10) if (persoon.isLid if (persoon.isJarig()) return 0 ; return normalePrijs(persoon) } prijs(persoon) { if (leeftijd ≤ 10) if (persoon.isLid if (persoon.isJarig()) return 0 ; return normalePrijs(persoon) } if (nietZommer(datum ) ) prijs = 0.8 * prijs

28 Vereenvoudiging van structuur remove control flag 28 gevonden = false product = null for (iter = V.iterator() ; iter.hasNext() && !gevonden ; ) { product = iter.next() gevonden = product.prijs() <= 10.0 } gevonden = false product = null for (iter = V.iterator() ; iter.hasNext() && !gevonden ; ) { product = iter.next() gevonden = product.prijs() <= 10.0 } for (Product p : V) { product = p if (product.prijs() <= 10.0) break } for (Product p : V) { product = p if (product.prijs() <= 10.0) break }

29 Vereenvoudiging van structuur Soms kun je “replace nested conditional with guarded clauses” doen. If-else voor normale flow met meerdere varianten Soms heb je normale flow met alternatieven die je liever ziet als afwijkende flows  minder expliciet in if-else 29 prijs (persoon) { normaleprijs = 30 ; if (persoon.leeftijd ≤ 4) return 0 else if (!dag.isWeekend ()) return normaleprijs+10 ; else return normaleprijs ; } prijs (persoon) { normaleprijs = 30 ; if (persoon.leeftijd ≤ 4) return 0 else if (!dag.isWeekend ()) return normaleprijs+10 ; else return normaleprijs ; } prijs() { if (dag.isWeekend()) return 40.0 else return 30.0 } prijs() { if (dag.isWeekend()) return 40.0 else return 30.0 }

30 Vereenvoudiging van structuur Soms heb je normale flow met een of meer afwijkende flows  minder expliciet in if-else 30 prijs (persoon) { normaleprijs = 30 if (persoon.leeftijd ≤ 4) return 0 if (datum.isOpenDag()) normaleprijs += 10 return normaleprijs } prijs (persoon) { normaleprijs = 30 if (persoon.leeftijd ≤ 4) return 0 if (datum.isOpenDag()) normaleprijs += 10 return normaleprijs } prijs (persoon) { normaleprijs = 30 ; if (persoon.leeftijd ≤ 4) return 0 else if (!dag.isWeekend ()) return normaleprijs+10 ; else return normaleprijs ; } prijs (persoon) { normaleprijs = 30 ; if (persoon.leeftijd ≤ 4) return 0 else if (!dag.isWeekend ()) return normaleprijs+10 ; else return normaleprijs ; }

31 Generalisatie Verplaatsen van features binnen een hiërarchie. Splitsen en samenvoegen Vervangen van inheritence met delegatie, of andersom 31 Optimaliseer je inheritence structuur.

32 Pull up / pull down 32 Product naam Product naam Appel korting() Appel korting() Koffie korting() Koffie korting() Product naam korting() Product naam korting() Appel Koffie pull up Product naam korting() Product naam korting() Appel Koffie als korting() eigenlijk alleen relevant is voor appel Product naam Product naam Appel korting() Appel korting() Koffie pull down

33 Extract sub/super class 33 Product naam korting() Product naam korting() korting wordt alleen door sommige instanties gebruikt Product naam Product naam KortingProduct korting() KortingProduct korting() extract subclass Product naam prijs getIcoon () setIcoon(i) resetIcoon() Product naam prijs getIcoon () setIcoon(i) resetIcoon() Persoon naam getIcoon () setIcoon(i) resetIcoon() Persoon naam getIcoon () setIcoon(i) resetIcoon() Product naam prijs Product naam prijs Persoon naam Persoon naam ItemMetIcoon getIcoon () setIcoon(i) resetIcoon() ItemMetIcoon getIcoon () setIcoon(i) resetIcoon() extract superclass Klassen met een subset van dezelfde features

34 Extract interface 34 Product naam getPrijs() getActie() getKorting() Product naam getPrijs() getActie() getKorting() WebShop Factuur meerdere klantklassen die dezelfde subset van methodes van Product gebruiken Product naam Product naam PrijsItem getPrijs() getActie() getKorting() PrijsItem getPrijs() getActie() getKorting() extract interface

35 Collapse hierarchy 35 Appel GroenAppel ze blijken niet veel van elkaar verschillen. Appel

36 replace delegation with inheritence, en omgekeerd 36 Product naam prijs Product naam prijs ItemMetIcoon getIcoon () setIcoon(i) resetIcoon() … ItemMetIcoon getIcoon () setIcoon(i) resetIcoon() … Product naam prijs Product naam prijs ItemMetIcoon getIcoon () setIcoon(i) resetIcoon() ItemMetIcoon getIcoon () setIcoon(i) resetIcoon() 1 0..1 als Product blijkt slechts een relatief klein deel van ItemMetIcoon gebruikt als Product blijkt veel werk naar ItemMetIcoon delegeert

37 Tools Liever laat je refactoringstappen geautomatiseerd door een tool veel sneller dan handmatig kans om fout te maken is veel kleiner. maar sommige refactoringstappen zijn moeilijk te automatiseren (zoals replace cond with polymorhpism) Ondersteunt door IDEs, maar ondersteuning varieert Eclipse, Netbeans, IntelliJ Elimineer de noodzaak van testing niet. 37


Download ppt "Introductie Verschillende refactoringstappen Refactoring."

Verwante presentaties


Ads door Google