1 / 39

Refactoring and Code Smells

Refactoring and Code Smells. Why do good developers write bad software?. Requirements change over time, making it hard to update your code (leading to less optimal designs) Time and money cause you to take shortcuts

arnoldadams
Télécharger la présentation

Refactoring and Code Smells

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Refactoring and Code Smells

  2. Why do good developers write bad software? • Requirements change over time, making it hard to update your code (leading to less optimal designs) • Time and money cause you to take shortcuts • You learn a better way to do something (the second time you paint a room, it’s always better than the first because you learned during the first time!) • Two questions: • How do we fix our software? • How do we know our software is “bad”… when it works fine!

  3. Refactoring • Refactoring modifies software to improve its readability, maintainability, and extensibility without changing what it actually does. • External behavior does NOT change • Internal structure is improved

  4. Refactoring • The goal of refactoring is NOT to add new functionality • The goal is refactoring is to make code easier to maintain in the future: • It’s an overhead activity • There’s a risk of over-engineering

  5. Where did Refactoring come from ? • Two of the first people to recognize the importance of refactoring were Kent Beck and Ward Cunningham • They worked with Smalltalk from the 80's onward. • Refactoring has always been an important element in the Smalltalk community. • Ralph Johnson's work with refactoring and frameworks has also been an important contribution.

  6. Why Refactor ? • Improves the design of software Without refactoring the design of the program will decay. Poorly designed code does the same thing in different places • Easier to understand In most software development environments, somebody else will eventually have to read your code so it becomes easy for others to comprehend.

  7. Motivation • We refactor because we understand getting the design right the first time is hard and you get many benefits from refactoring: • Code size is often reduced • Confusing code is restructured into simpler code • Both of these greatly improve maintainability, which is required because requirements always change! Coming up: Refactoring: Complex Example

  8. When to Refractor ? • When you add a function Sometimes the existing design does not allow you to easily add the feature. • When you need to fix a bug If you get a bug report it’s a sign the code needs refactoring because the code was not clear enough for you to see the bug in the first place. • When a code review and/or metrics spot a problem You need to detect code smells.

  9. Danger! • Refactoring CAN introduce problems, because anytime you modify software you may introduce bugs! • Management thus says: • Refactoring adds risk! • It’s expensive – we’re spending time in development, but not “seeing” any external differences? And we still have to retest… • Why are we doing this?

  10. Costs of Refactoring Language / Environment : Depends on how well the operations on the source code are supported. But in general the cost of applying the basic text modifications should be small. Testing : Refactoring relies heavily on testing after each small modification, which entails having a solid test suite of tests for the whole system. Refactoring and testing manually are incompatible. Also, test may need to be tweaked as code is modified.

  11. Documentation : Costs of updating documentation of the project should not be underestimated as applying refactorings involves changes in interfaces, names, parameter lists and so on. All the documentation concerning these issues must be updated to the current state of development.

  12. When to put Off Refactoring ? • Concerned code is neither able to compile or to run in a stable manner, it might be better to throw it away and rewrite the software from scratch. • When a deadline is very close.

  13. Properties of Refactoring • Preserve Correctness • One step at a time • Frequent Testing

  14. Design Attributes to improve • Abstraction (information hiding) • Flexibility • Clarity • Irredundancy

  15. The steps taken when applying the refactoring should be: - Small enough to oversee the consequences they have. - Reproducible to allow others to understand them. - Generalized in a way that they are more a rule that can be applied to any structure. - Written down to allow sharing these steps and to keep a reference, with step by step instructions how to apply them.

  16. Refactoring: Simple Example Move a method: Motivation: A method is, or will be, using or used by more features of another class than the class on which it is defined. Technique: Create a new method with a similar body in the class it uses most. Either turn the old method into a simple delegation, or remove it altogether.

  17. Refactoring: More Complex Example Introduce Null Object Motivation: You have many checks for null Technique: Replace the null value with a null object. Customer c = findCustomer(...); ... if (customer == null) { name = “occupant” } else { name = customer.getName() } if (customer == null) { …

  18. public class NullCustomer extends Customer { public String getName() { return “occupant” } ------------------------------------------------------------ Customer c = findCustomer()name = c.getName() Completely eliminated the if statement by replacing checks for null with a null object that does the right thing for “null” values. JP’s comment: hum...

  19. Refactoring Example: Replace Conditional with Polymorphism Motivation: You have a conditional that chooses different behavior depending on the type of an object. Technique: Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract. double getSpeed() { switch (_type) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts; case NORWEGIAN_BLUE: return ...; } throw new RuntimeException ("Should be unreachable"); }

  20. Refactoring Example: Replace Conditional with Polymorphism

  21. Add Parameter Change Association Reference to value Value to reference Collapse hierarchy Consolidate conditionals Procedures to objects Decompose conditional Encapsulate collection Encapsulate downcast Encapsulate field Extract class Extract Interface Extract method Extract subclass Extract superclass Form template method Hide delegate Hide method Inline class Inline temp Introduce assertion Introduce explain variable Introduce foreign method A few Refactorings

  22. Add a Parameter : When a method needs more information from its caller. • Bi-directional Association to Unidirectional : • We have a two-way association but one class no longer needs features from the other. • Change Reference to Value : • When we have a reference object that is small, immutable, and awkward to manage. • Collapse Hierarchy : • As superclass and subclass are not very different.

  23. Consolidate conditionals double disabilityAmount() { if (_seniority < 2) return 0; if (_monthsDisabled > 12) return 0; if (_isPartTime) return // compute the disability amount double disabilityAmount() { if (isNotEligibleForDisability()) return 0; // compute the disability amount

  24. Decompose Conditionals When we have a complicated conditional (if-then-else) statement. if (date.before (SUMMER_START) || date.after(SUMMER_END)) charge = quantity * _winterRate + _winterServiceCharge; else charge = quantity * _summerRate; if (notSummer(date)) charge = quantity * _winterRate + _winterServiceCharge; else charge = summerCharge (quantity);

  25. Encapsulate Downcast When a method returns an object that needs to be downcasted by its callers. Reading lastReading() { return (Reading) readings.lastElement(); } Object lastReading() { return readings.lastElement(); } Extract Class When we have one class doing the work that should be done by two.

  26. Extract Method : When we have a code fragment that can be grouped together. void printOwing() { printBanner(); printDetails(getOutstanding());} void printOwing() { printBanner(); //print details System.out.println ("name: " + _name); System.out.println ("amount " + getOutstanding()); } void printDetails (double outstanding) { System.out.println ("name:" + _name); System.out.println ("amount:" + outstanding); }

  27. How do I identify code to refactor? • Martin Fowler uses “code smells” (Kent Beck’s term) to identify when to refactor. • Code smells are bad things done in code, somewhat like bad patterns in code • Many people have tied code smells to specific refactoring(s) to fix the smell

  28. Bad Smells in Code • Parallel Interface Hierarchies • Lazy Class • Speculative Generality • Temporary Field • Message Chains • Middle Man • Inappropriate Intimacy • Incomplete Library Class • Data Class • Refused Bequest • Duplicated Code • Long Method • Large Class • Long Parameter List • Divergent Change • Shotgun Surgery • Feature Envy • Data Clumps • Primitive Obsession • Switch Statements • Many more smells at: • http://c2.com/cgi/wiki?CodeSmell

  29. Code Smells (1) • Duplicated Code • bad because if you modify one instance of duplicated code but not the others, you (may) have introduced a bug! • Long Method • long methods are more difficult to understand • performance concerns with respect to lots of short methods are largely obsolete

  30. Code Smells (2) • Large Class • classes try to do too much, which reduces cohesion • Long Parameter List • hard to understand, can become inconsistent • Divergent Change • Related to cohesion: one type of change requires changing one subset of methods, another type of change requires changing another subset

  31. Code Smells • Lazy Class • A class that no longer “pays its way” • e.g. may be a class that was downsized by a previous refactoring, or represented planned functionality that did not pan out • Speculative Generality • “Oh I think we need the ability to do this kind of thing someday” • Temporary Field • An attribute of an object is only set in certain circumstances: an object should need all of its attributes

  32. Code Smells • Data Class • These are classes that have fields, getting and setting methods for the fields, and nothing else; they are data holders, but objects should be about data AND behavior • Refused Bequest • A subclass ignores most of the functionality provided by its superclass • Subclass may not pass the “IS-A” test • Comments (!) • Comments are sometimes used to hide bad code • “…comments often are used as a deodorant” (!)

  33. Why use them? • Code smells and refactoring are techniques to help you discover problems in design and implementation and apply known solutions to these problems • See smell/refactory cheat sheet • Should they be used all the time? You should always think about them, but only apply them when they make sense… sometimes you need a long method… but think about it to make sure!

  34. Adding safety • Remember that making these changes incurs some risk of introducing bugs! • To reduce that risk • You must test constantly – using automated tests wherever possible • Use refactoring patterns http://www.refactoring.com/catalog/index.html • Use tools! Netbeans and Eclipse both support basic refactoring (http://wiki.netbeans.org/Refactoring)

  35. Few solutions to Bad Smells • Duplicated Code If you see the same code structure in more than one place, you can be sure that your program will be better if you find a way to unify them. solution: perform EXTRACT METHOD and invoke the code from both places. • Long Method The longer a procedure is the more difficult it is to understand. solution: perform EXTRACT METHOD or Decompose Conditional or Replace Temp with Query.

  36. Large class When a class is trying to do too much, it often shows up as too many instance variables. solution: perform EXTRACT CLASS or SUBCLASS • Long Parameter List With objects you don't need to pass in everything the method needs, instead you pass in enough so the method can get to everything it needs solution: Use REPLACE PARAMETER with METHOD when you can get the data in one parameter by making a request of an object you already know about.

  37. Shotgun Surgery This situation occurs when every time you make a kind of change, you have to make a lot of little changes to a lot of different classes. solution: perform MOVE METHOD/FIELD or INLINE Class bring a whole bunch of behavior together. • Feature Envy It is a method that seems more interested in a class distinct from the one that it is in. solution: perform MOVE METHOD or EXTRACT METHOD on the jealous bit and get it home.

  38. Switch Statements They are generally scattered throughout a program. If you add or remove a clause in one switch, you often have to find and repair the others too. solution: Use EXTRACT METHOD to extract the switch statement and then MOVE METHOD to get it into the class where the polymorphism is needed.

  39. Bibliography • Martin Fowler, Refactoring: improving the design of existing code, Addison Wesley, 1999, ISBN 0-201-48567-2

More Related