Refactoring, - UdL...
Transcript of Refactoring, - UdL...
Refactoring
So#ware Quality Quality Audit and Cer4fica4on
Master in Computer Engineering
Roberto García ([email protected])
Introduc4on
• When considering soAware evolu4on, the key dis4nc4on is: – Program's quality improves or degrades?
• Treat modifica4ons as opportuni4es to improve quality
• Monitor project quality regularly – Warning if it degrades
Introduc4on
• A second dis4nc4on: – Changes made during construc8on – Changes made during maintenance
• Construc8on changes are usually made by the original developers – Done under less pressure than maintenance changes
• Profit soAware changes to improve the code and its internal quality, so that future changes are easier
Introduc4on
• The key strategy in achieving soAware evolu8on is refactoring
• Fowler (1999): – “a change made to the internal structure of the soAware to make it easier to understand and cheaper to modify without changing its observable behaviour”
• Reasons to Refactor – Some4mes code degenerates under maintenance, and some4mes the code just wasn't very good in the first place
When Refactoring
• Warning signs that indicate where refactorings are needed: – Code is duplicated – A rou8ne is too long
• In object-‐oriented programming, rarely rou4nes longer than a screen
– A loop is too long or too deeply nested • Loop innards tend to be good candidates for being converted into rou4nes
– A class has poor cohesion • It takes ownership for a mix of unrelated responsibili4es
– A class interface does not provide a consistent level of abstrac8on – A parameter list has too many parameters – Changes require parallel modifica4ons to mul4ple classes
When Refactoring
• Refactoring warning signs (cont.): – Related data items that are used together are not organized into
classes – A rou8ne uses more features of another class than of its own class – A primi8ve data type is overloaded
• If using Integer for money, temperature,… create separate classes for them
– A class doesn't do very much • Move responsibili4es to other and remove it
– A chain of rou4nes just passes data along – A middleman object isn't doing anything – One class knows too much about another
• Stronger encapsula4on preferred to weaker
When Refactoring
• Refactoring warning signs (cont.): – A rou4ne has a poor name
• Rename it wherever defined or used and recompile
– Data members are public • Hide data behind ge^ers and se^ers
– A subclass uses only a small percentage of its parents' rou8nes • Change rela4on to “has-‐a”
– Comments are used to explain difficult code – Global variables are used – A rou4ne uses setup code before a rou4ne call or takedown code
a#er a rou4ne call • Avoid crea4ng an object just to call it and aAer that unpack some of its fields
– A program contains code that seems like it might be needed someday
Specific Refactorings Data • Main ones from “Refactoring” (Fowler 1999) – h^p://www.refactoring.com/catalog
• Data Refactorings: – Replace a magic number with a named constant
• Replace literal like 3.14 with named constant like PI – Rename a variable with a clearer or more informa8ve name • The same applies to renaming constants, classes and rou4nes
– Move an expression inline • Replace an intermediate variable that was assigned the result of an expression with the expression itself
Data Refactoring Inline Temps
!!double basePrice = anOrder.basePrice();!return (basePrice > 1000)!!!return (anOrder.basePrice() > 1000)!
Specific Refactorings Data • Data Refactorings (cont.):
– Replace an expression with a rou8ne • Replace an expression with a rou4ne (usually so that the expression isn't duplicated in the code)
– Introduce an intermediate variable • Assign an expression to an intermediate variable whose name summarizes the purpose of the expression
– Convert a mul8use variable to mul8ple single-‐use variables • Create separate variables for each usage with a more specific name
– Use a local variable for local purposes rather than a parameter – Convert a data primi8ve to a class
• If a data primi4ve needs addi4onal behaviour (e.g. Temperature)
Data Refactoring Split Temporary Variable
double temp = 2 * (_height + _width);!System.out.println (temp);!temp = _height * _width;!System.out.println (temp);!!!final double perimeter = 2 * (_height + _width);!System.out.println (perimeter);!final double area = _height * _width;!System.out.println (area);!
Specific Refactorings Data • Data refactorings (cont.): – Convert a set of type codes to a class or an enumera8on • Like: const int SCREEN = 0; const int PRINTER = 1; const int FILE = 2;
– Convert a set of type codes to a class with subclasses • A base class for the type with subclasses for each type code
– Change an array to an object • If different elements in the array are different types, create an object with fields
– Replace a tradi8onal record with a data class • Centralize opera4ons concerning the record (error checking, persistence,…)
Data Refactoring Replace Type Code with Subclasses
Specific Refactorings Statement-‐Level • Statement-‐Level Refactorings: – Decompose a boolean expression
• Simplify a boolean expression by introducing well-‐named intermediate variables
– Move a complex boolean expression into a well-‐named boolean func8on • Improve readability and if used more than once, eliminate parallel modifica4ons
– Consolidate fragments that are duplicated within different parts of a condi8onal • Move code repeated at the end of an else and if block to the end of the en4re if-‐then-‐else block
– Use break or return instead of a loop control variable
Data Refactoring Decompose Condi4onal
!if (date.before (SUMMER_START) || date.after(SUMMER_END))!! !charge = quantity * _winterRate + _winterServiceCharge;!else !! !charge = quantity * _summerRate;!!!!if (notSummer(date))!! !charge = winterCharge(quantity);!!else!! !charge = summerCharge (quantity);!
Specific Refactorings Statement-‐Level • Statement-‐Level Refactorings (cont.):
– Return as soon as you know the answer instead of assigning a return value within nested if-‐then-‐else statements
– Replace condi8onals (especially repeated case statements) with polymorphism • Implement condi4onal logic into inheritance hierarchy and polymorphic rou4ne calls
– Create and use null objects instead of tes8ng for null values • Consider moving the responsibility for handling null values out of the client code and into the class – Example: referring to a resident whose name is not known as "occupant."
Have the Customer class define the unknown resident as "occupant" instead of having Customer's client code repeatedly test for whether the customer's name is known and subs4tute "occupant" if not
Data Refactoring Decompose Condi4onal double getSpeed() {! switch (_type) {! case EUROPEAN: return getBaseSpeed();! case AFRICAN: return getBaseSpeed() - getLoadFactor();! case NORWEGIAN: return getBaseSpeed(_voltage);! }! throw new RuntimeException ("Should be unreachable");!}!
Specific Refactorings Rou4ne-‐Level • Rou4ne-‐Level Refactorings:
– Extract rou8ne/extract method • Remove inline code from one rou4ne, and turn it into its own rou4ne
– Move a rou8ne's code inline • Take code from a rou4ne whose body is simple and self-‐explanatory, and move that rou4ne's code inline where it is used
– Convert a long rou8ne to a class • For a long rou4ne, turn it into a class and factor it into mul4ple rou4nes
– Add a parameter • When rou4ne needs more informa4on from its caller, add a parameter
– Remove a parameter • If a rou4ne no longer uses a parameter, remove it
Data Refactoring Extract Method
void printOwing() {!! !printBanner();!!! !//print details!! !System.out.println ("name: !" + _name);!! !System.out.println ("amount!" + getOutstanding());!!}!!!!!void printOwing() {!! !printBanner();!! !printDetails(getOutstanding());!!}!!!void printDetails (double outstanding) {!! !System.out.println ("name: !" + _name);!! !System.out.println ("amount!" + outstanding);!!}!
Specific Refactorings Rou4ne-‐Level • Rou4ne-‐Level Refactorings (cont.):
– Separate query opera8ons from modifica8on opera8ons • If GetTotals() changes an object's state, separate the state-‐changing func4onality
– Combine similar rou8nes by parameterizing them • Example: similar rou4nes differ only with respect to a constant value, combine them and
pass the value as a parameter – Separate rou8nes whose behaviour depends on parameters passed in
• Consider the reverse of the previous refactoring if the rou4ne is too complex – Pass a whole object rather than specific fields
• Instead of passing several values from the same object – Pass specific fields rather than a whole object
• If the object is created just to pass it to a rou4ne – Encapsulate downcas8ng
• If a rou4ne returns an object, return the most specific type it knows about, specially for iterators, collec4ons,…
Data Refactoring Encapsulate Downcast & Parameterise Method
Object lastReading() {!! !return readings.lastElement();!}!!!Reading lastReading() {!! !return (Reading) readings.lastElement();!}!
Specific Refactorings Class Implementa4on • Class Implementa4on Refactorings: – Change value objects to reference objects
• Avoid crea4ng and maintaining numerous copies of large or complex objects, only one master copy (the value object) and the rest of the code uses references to that object (reference objects)
– Change reference objects to value objects • Avoid excessive reference housekeeping for small or simple objects, make them value objects
– Replace virtual rou8nes with data ini8aliza8on • Subclasses that vary according to constant values they return, avoid overriding member rou4nes in the derived classes, ini4alise them with appropriate constant values and keep base class generic code
Data Refactoring Change Value to Reference & Reference to Value
Specific Refactorings Class Implementa4on • Class Implementa4on Refactorings (cont.):
– Change member rou8ne or data placement • Consider general changes in an inheritance hierarchy to eliminate duplica4on in derived classes: – Pull a rou4ne up into its superclass – Pull a field up into its superclass – Pull a constructor body up into its superclass
• Other changes made to support specializa4on in derived classes: – Push a rou4ne down into its derived classes – Push a field down into its derived classes – Push a constructor body down into its derived classes
– Extract specialized code into a subclass • Move class code used by only a subset of its instances into a subclass
– Combine similar code into a superclass • Combine similar code from subclasses into the superclass
Data Refactoring Pull Up Constructor Body class Manager extends Employee...!
!public Manager (String name, String id, int grade) {!! !_name = name;!! !_id = id;!! !_grade = grade;!!}!!!!!public Manager (String name, String id, int grade) {!! !super (name, id);!! !_grade = grade;!!}!
Specific Refactorings Class Interface • Class Interface Refactorings:
– Move a rou8ne to another class – Convert one class to two
• If a class has two or more dis4nct areas of responsibility – Hide a delegate
• Some4mes Class A calls Class B and C, when really it should call only Class B and B should call C
– Remove a middleman • If Class A calls Class B and B calls Class C, have Class A call Class C directly
– Replace delega8on with inheritance • If a class exposes every public rou4ne of a delegate class, inherit from the delegate class instead of just using the class
• Also possible to replace inheritance with delega4on, if inheritance effects not desired
Data Refactoring Replace Delega4on with Inheritance
Specific Refactorings Class Interface • Class Interface Refactorings (cont.):
– Introduce a foreign rou8ne • If a class needs an addi4onal rou4ne and you can't modify the class to provide it, you can
create a new rou4ne within the client class that provides that func4onality. – Introduce an extension class
• A unmodifiable class needs addi4onal rou4nes, create a new class subclassing the original class and adding new rou4nes or wrapping the class and exposing the new rou4nes
– Encapsulate an exposed member variable • Make public data private and expose it through a rou4ne
– Remove Set() rou8nes for fields that cannot be changed – Hide rou8nes that are not intended to be used outside the class – Encapsulate unused rou8nes
• Rou4nely using only a por4on of a class's interface, create a new interface that exposes only those necessary rou4nes
– Collapse a superclass and subclass if their implementa8ons are very similar
Data Refactoring Introduce Foreign Method
!Date newStart = new Date (!previousEnd.getYear(),!! ! ! ! ! ! ! !previousEnd.getMonth(), !! ! ! ! ! ! ! !previousEnd.getDate() + 1);!
!!!!!
!Date newStart = nextDay(previousEnd);!!
!private static Date nextDay(Date arg) {!! !return new Date ( !arg.getYear(),!! ! ! ! ! ! !arg.getMonth(), !! ! ! ! ! ! !arg.getDate() + 1);!!}!
Specific Refactorings System-‐Level • System-‐Level Refactorings
– Change unidirec8onal class associa8on to bidirec8onal class associa8on • For two classes that need to use each other's features but only one knows
about the other class, change them so that they both know about each other – Change bidirec8onal class associa8on to unidirec8onal class
associa8on • For two classes that know about each other's features but only one really
needs to know, change them so that one knows about the other but not vice versa.
– Provide a factory method rather than a simple constructor • Use a factory method when need to create objects based on a type code or
when want to work with reference objects rather than value objects – Replace error codes with excep8ons or vice versa
• Depending on your error-‐handling strategy, make sure the code is using the standard approach
Data Refactoring Replace Constructor with Factory Method & Change Bidirec4onal Associa4on to Unidirec4onal !!Employee (int type) {!
!_type = type;!}!!!!static Employee create(int type) {!
!return new Employee(type);!}!
Refactoring Safety
• If misused, can cause more harm than good • A few simple guidelines to prevent missteps: – Save the code you start with – Keep refactorings small
• Small enough so easy to understand all the impacts of the changes – Do refactorings one at a 8me
• Recompiling and retes4ng before the next one – Make a list of steps you intend to take – Keep a list of intended refactorings
• Those not needed immediately – Make frequent checkpoints
• Save checkpoints at various steps in a refactoring session
Refactoring Safety
• Guidelines (cont.): – Retest
• Reviews of changed code should be complemented by retests
– Add test cases • Add new unit tests for the new code. Remove obsolete
– Review the changes • Pair programming, formal / informal reviews,…
Refactoring Strategies
• Guidelines for deciding priority refactorings: – Refactor when you add a rou8ne
• Check if related rou4nes are well organized. If not, refactor them. – Refactor when you add a class
• Opportunity to refactor classes closely related to the added class – Refactor when you fix a defect
• From fixing a bug to improving other code prone to similar defects – Target error-‐prone modules
• Modules that concentrate defects from tes4ng or review – Target high-‐complexity modules – Define an interface between clean code and ugly code, and then move code across the interface • A common problem with legacy systems is poorly wri^en produc4on code, which must remain opera4onal at all 4mes
Key Points
• Program changes are a fact of life both during ini4al development and aAer ini4al release
• SoAware can either improve or degrade as it's changed. The Cardinal Rule of SoAware Evolu4on is that internal quality should improve with code evolu4on
• One key to success in refactoring is learning to pay a^en4on to the numerous warning signs or smells that indicate a need to refactor.
• Another key to refactoring success is learning numerous specific refactorings
• A final key to success is having a strategy for refactoring safely. Some refactoring approaches are be^er than others
• Refactoring during development is the best chance you'll get to improve your program, to make all the changes you'll wish you'd made the first 4me. Take advantage of these opportuni4es during development!
Ac4vity
• Refactor the MiniProject source code following the strategies: – Target module/s with higher concentra8on of defects during review and tes4ng
– Target high-‐complexity module/s • For the selected modules, perform at least 10 refactorings
– The more diverse in kind and the more produc8ve towards soAware quality, the beYer
• Deliverables: – Through Campus Virtual upload document describing all refactorings: • class name, method name, line number, ini4al code, refactoring applied, result code, mo4va4on,…
– Update resul4ng code at Github