Presentation is loading. Please wait.

Presentation is loading. Please wait.

Refactoring Compl. Bad smells in your software Code duplication Long operations, big classes Operations with lots of parameters Divergent or shotgun fixes.

Similar presentations


Presentation on theme: "Refactoring Compl. Bad smells in your software Code duplication Long operations, big classes Operations with lots of parameters Divergent or shotgun fixes."— Presentation transcript:

1 Refactoring Compl

2 Bad smells in your software Code duplication Long operations, big classes Operations with lots of parameters Divergent or shotgun fixes during maintenance Feature envy You use switch (*) fake attributes in your class comments inside an operation (Eh? Im sorry!?) 2

3 Suboptimaal software Complexity & flexibility of your software components are not optimal. maintenance cost bugs fix, adapting features, adding features Dont underestimate maintenance. 3 Mitigated by: invest in a good design, employ design patterns Still, under pressure (deadline!) the temptation to do dirty solving is big... Lets take a look at refactoring

4 Refactoring 4 Refactoring is a transformation on the source code of a software to make it easier to understand and to maintain. The transformation must preserve the softwares observable behavior. So, performance is not a consideration in refactoring. This is not to say that performance is unimportant. But refactoring is limited to achieve the above mentioned goals. Fowler, With contrib. from e.g. Erich Gamma (GOF) & Kent Beck (JUnit) Belong to basic vocab of sw. engineers.

5 How to do it? Formal theory of refactoring? sadly none practical enough. So, we do it in a series of small steps, like... Rely on testing (!) Intergrate it structurally into your maintenance workflow 5 Keep in mind that the goal of refactoring is not a cosmetic make-over, though some of the steps may look cosmetics. The goal is: mitigating your long term maintenance. I will mark the refactoring names like this: Extract Method

6 Example 6 Film - title - priceCode : int RentInfo - numOfDays * film Customer - name - CustPoints + rent(film) + back(film) + mkInvoice() * Well keep getter and setter implicit. RentInfo - numOfDays * * rentInfos film 1 Three price categories, encoded as constants: CHILDREN = 0 STANDARD = 1 NEW = 2 C: Customer F1 : Film F2 : Film 3d : RentInfo 7d : RentInfo

7 7 mkInvoice () { totPrice = 0 pts = 0 println(Invoice for + name) for (RentInfo v : rentInfos) { // calculate price for a single film v_price = 0 switch (v.getFilm().getPriceCode()) { case Film.STANDARD : v_price = 3 euro * v.numOfdays ; break case Film.NEW : v_price = 5 euro * v.numOfdays ; break case Film.CHILDREN : v_price = 2 euro * v.numOfdays } totPrice += v_price // calculate earned points pts++ if (v.getFilm().getPriceCode()==Film.NEW) pts++ println(Film + v.getName() +, price = + v_price) } println(Tot price = + totPrice) println(Points earned = + pts) } Bad smells? Lets rework this, but in small steps refactoring.

8 Extract method 8 mkInvoice () { totPrice = 0 pts = 0 println(Invoice for + name) for (RentInfo v : rentInfos) { // calculate price for a single film v_price = 0 switch (v.getFilm().getPriceCode()) { case Film.STANDARD : v_price =... ; break case Film.NEW : v_price =... ; break case Film.CHILDREN : v_price =... } totPrice += v_price // calculate earned points pts++ if (v.getFilm().getPriceCode()==Film.NEW) pts++ println(Film + v.getName() +, price = + v_price) } println(Tot price = + totPrice) println(Points earned = + pts) } promote a fraction of code to a method. (comments often give hint)

9 Extract method 9 mkInvoice () { totPrice = 0 pts = 0 println(Invoice for + name) for (RentInfo v : rentInfos) { // calculate price for a single film v_price = 0 switch (v.getFilm().getPriceCode()) { case Film.STANDARD : v_price =... ; break case Film.NEW : v_price =... ; break case Film.CHILDREN : v_price =... } totPrice += v_price // calculate earned points pts++ if (v.getFilm().getPriceCode()==Film.NEW) pts++ println(Film + v.getName() +, price = + v_price) } println(Tot price = + totPrice) println(Points earned = + pts) } calculatePrice(v) { v_price = 0 switch (v.getFilm().getPriceCode()) { case Film.STANDARD : v_price =... ; break case Film.NEW : v_price =... ; break case Film.CHILDREN : v_price =... } return v_price } v_price = calculatePrice(v)

10 Extract method 10 mkInvoice () { totPrice = 0 pts = 0 println(Invoice for + name) for (RentInfo v : rentInfos) { // calculate price for a single film.... totPrice += v_price // calculate earned points pts++ if (v.getFilm().getPriceCode()==Film.NEW) pts++ println(Film + v.getName() +, price = + v_price) } println(Tot price = + totPrice) println(Points earned = + pts) } ?? Take sufficient context along caculateEarnedPoints(v,pts) { pts++ if (v.getFilm().getPriceCode()==Film.NEW) pts++ return pts } pts = calculateEarnedPoints(v,pts)

11 After extraction 11 mkInvoice () { totPrice = 0 pts = 0 println(Invoice for + name) for (RentInfo v : rentInfos) { v_price = calculatePrice(v) totPrice += v_price pts = calculateEarnedPoints(v,pts) println(Film + v.getName() +, price = + v_price) } println(Tot price = + totPrice) println(Points earned = + pts) } these were comments! Now self-documenting. This is easier to understand now, no?

12 Dont we all love temporary variables? 12 int p = calculatePrice(v) totPrice += p println(Film + v.getName() +, price = + p) Conceptually, v_price is just use to hold the result of calculatePrice. They are synonymous; but we have introduced it to get a bit of performance. But it does complicate your code! (your mind have to keep track of one more item in your program) totPrice += calculatePrice(v) println(Film + v.getName() +, price = + calculatePrice(v)) Remove temp How about performance? Balancing between maintenance and performance. 90% of the time, your software is doing just 10% of your code. User profiler to locate this 10%, and target your performance optimization on this 10%.

13 Features envy 13 calculatePrice(v) { v_price = 0 switch (v.getFilm().getPriceCode()) { case Film.STANDARD : v_price = 3 euro * v.numOfdays ; break case Film.NEW : v_price = 5 euro * v.numOfdays ; break case Film.CHILDREN : v_price = 2 euro * v.numOfdays } return v_price } Customer is doing too much work on data obtained from Film. Shouldnt Film does this work instead?? risk of shot-gun fixes in the future Move method Customer calculatePrice(v) Film calculatePrice(v)

14 Why is this switch bad? 14 calculatePrice(v) { v_price = 0 switch (v.getFilm().getPriceCode()) { case Film.STANDARD : v_price =... ; break case Film.NEW : v_price =... ; break case Film.CHILDREN : v_price =... } return v_price } Film calculatePrice()v Adding a new kind of film forces you to change calculatePrice divergent fix. Film calculatePrice(v) Film calculatePrice(v) StandardFilm NewFilm ChildrenFilm Solving it with inheritence. Unfortunaly in this example, film-type should be able to change dynamically! So you need a different solution.

15 Replace type-code with strategy + polymorphism 15 calculatePrice() { v_price = 0 switch (v.getFilm().getPriceCode()) { case Film.STANDARD : v_price =... ; break case Film.NEW : v_price =... ; break case Film.CHILDREN : v_price =... } return v_price } Film calculatePrice(v) FilmPriceStrategy calculatePrice(v) StandardPrice NewPrice ChildrenPrice 1 calculatePrice(v) { return priceStrg.calculatePrice(v) } priceStrg You can change the strategy dynamically: film.priceStrg = new NewPrice() film.priceStrg = new StandardPrice()

16 Fowlers refactoring set Streamlining methodes (9) Moving features (8) Organizing data (16) Simplifying conditional statements (8) Simplifying method calls (15) Generalization (12) 16

17 Streamlining methods extract method – inverse: inline method but youll risk code duplication. To limit: apply if the body is just as clear as the methods name, and is not called in too many other methods. eliminate and introduce temp – temp is a form of indirection – furthermore makes method extraction more difficult as you have to move its context as well 17 Fowler: long methods are often the sources of problems (he prefers more methods; but short)

18 Temp introduce explaining variable and eliminate temp replace temp with query you cant elimimate all temps loop counter, accumulation var. 18 double sellPrice= product.getPrice() * 1.5 p = sellPrice > 10.0 p = product.getPrice() * 1.5 > 10.0 expression is too complex p = sellPrice() > 10.0

19 You have too many parameters … Introduce parameter object 19 price(date, age, isMember, discountCard, isOpenDay) price(personProfile, dateProfile) personProfile age isMember discountCard dateProfile date isOpenday

20 Moving Features Main tool: move method/field (you have seen this) spliting and combining classes introducing and eliminating delegation 20 Distribute the work (which class does what). A class that does too much is difficult to understand.

21 Split and combine Extract class: a class C does too much. Identify a part of C that can be group to its own class. inverse: inline class. 21 Person name age street housenr postcode … printAddress() Person name age... Address street housenr postcode printAddress() 1 1

22 Delegate Hide delegate remove middle man 22 Person Address Postcode getCity() address.getPostcode().getCity() Person Address getCity() Postcode getCity() address. getCity() getCity() { return postcode.getCity() } If Address is doing too mush delegation work instead of real work, then reverse: let the client access the delagate directly. A client (Person) of Address is calling Address delegate Hide this delegate

23 Streamlining conditionals Main tool : extract method on conditionals – decompose conditional, consolidate conditional, Simplification on the structure – remove control flag – replace conditional with polymorphism (you have seen this) – replace nested conditional with guard clauses 23 Complex domain logics translate to complex conditional statements, which are often difficult to understand (hence error prone).

24 Extract method on conditions 24 if (date.before(SUMMER_BEGIN) || date.after(SUMMER_END) ) price = 0.8 * price +... // some complicated expr if (notSummer(date ) ) price = 0.8 * price... A complicate conditional: Decompose conditional if (notSummer(date ) ) price = summerPrice(...)

25 Extract method on conditions 25 price(person) { if (isFree(person)) return 0 ; return stdPrice(person) } price(person) { if (person.age 10) if (person.isMember) if (person.isBirthday()) return 0 ; return stdPrice(person) } Consolidate conditional expression giveCandy(person) { if (person.age 10) return candy if (person.isMember) return candy if (person.isBirthday()) return candy return null } Series of if encoding or Cascade of if encoding and giveCandy(person) { if (candyEligible(person)) return candy ; return null }

26 Simplifying cond. structure 26 found = false product = null for (iter = V.iterator() ; iter.hasNext() && !found ; ) { product = iter.next() found = product.price() <= 10.0 } return product remove control flag control variable for (Product p : V) { product = p if (product.prijs() <= 10.0) break } return product if (product.prijs() <= 10.0) break

27 Simplifying cond. structure 27 giveMilk(person) { if (person is alergic) m = null else { m = new Milk() ; m.warmup() } return m } giveCandy(person) { if (person.age < 12) c = new MagicCandy() else c = new StdCandy() c.wrap() return c } If implementing two paths of your normal flow : An alternate flow seeks to break off the computation early, due to some exceptional condition. If implementing an alternate flow :

28 Simplifying cond. structure 28 giveMilk(person) { if (person is alergic) m = null else { m = new Milk() ; m.warmup() } return m } An alternate flow break off the computation early, due to some exceptional condition. If implementing an alternate flow : Here programmer also tried to make the program to have a single entry and exit point. That seems to be a good idea, no? Fowler: favor whichever is more readable. giveMilk(person) { if (person is alergic) return null m = new Milk() ; m.warmup() return m } Replace (nested) conditionals with guard clauses

29 Generalization Moving features within a hierarchy Spliting and combining hierarchy Replacing inheritence with delegation, or the other way around 29 Inheritence is importtant in OO; optimize your inheritence structure.

30 Pull up / pull down 30 Product name Apple discount() Coffee discount() Product name discount() Apple Coffee pull up Product name drink() Apple Coffee Product name Apple Coffee drink() pull down GreenApple VeryGreenApple GreenApple collapse hierarchy

31 Extract sub/super class 31 Product name discount() But discount is only relevant for a subset of the instances of Product. Product name DiscountedProduct discount() extract subclass Product price getIcon () setIcon(i) resetIcon() Person name getIcon () setIcon(i) resetIcon() Product price Person name ItemWithIcon getIcon () setIcon(i) resetIcon() extract superclass The classes have a common subset of features (Q: and what if we dont have multiple inheritance?)

32 Extract interface 32 Product name getPrice() getTax() getDiscount() … WebShop Invoice we have multiple client-classes that use the same subset of Products features Product name … > SellableProduct getPrice() getTax() getDiscount() extract interface > WebShop Invoice

33 Replace delegation with inheritence, or back 33 Product name price ItemWithIcon getIcon () setIcon(i) resetIcon() … Product name price ItemWithIcon getIcon () setIcon(i) resetIcon() If Product turns out to use only very few features of ItemWithIcon If Product turns out to delegate too much work to ItemWithIcon Replace delegation with Inheritence Replace Inheritence with delegation

34 Tools Refactoring cant be fully automated – some refactoring steps are difficult to automate some extent supported by IDEs, – Eclipse, Netbeans, IntelliJ – no correctness theory that is also practical enough you will have to rely on testing 34


Download ppt "Refactoring Compl. Bad smells in your software Code duplication Long operations, big classes Operations with lots of parameters Divergent or shotgun fixes."

Similar presentations


Ads by Google