Refactoring Presentation for: South Florida PHP Users Group By: Adam Culp Twitter: @adamculp http://www.geekyboy.com
Refactoring Based on popular “Refactoring” Improving The Design of Existing Code book, by Martin Fowler. We will start in the code with an example. Tips and descriptions will be handled during the example. In the example we are tasked with creating an HTML representation of the customer statement for a movie rental.
Refactoring Movie class With a new project we take a look at the current application a customer has requested to be changed. We start with the Movie class.
Refactoring Rental class Next we look at the Rental class.
Refactoring Customer class p1 With a new project we take a look at the current app that is about to be changed. Next we look at part 1 of the Customer class.
Refactoring Customer class p2 With a new project we take a look at the current app that is about to be changed. Next we look at part 2 of the Customer class.
Refactoring Not terrible for a small quick and dirty application, but if part of a larger application we have problems. Customer class doing far too much, and statement method is too long. Impossible to reuse the statement for an HTML representation. The customer also wants to be able to change how they classify movies. As experienced developers we know that customers will always come back six months from now and want it changed in some way.
Refactoring Rewrite vs Refactor Budget constraint Time constraint Rewrite is an excuse to NOT dig in someones code Refactor is the best teacher
Refactoring First step Build tests based on current “working” application, so we can verify if we break something. Pre-load database with data or create fixtures needed for tests, to ensure we have a constant to compare test results. If we are not starting with a working application you must first get the application working prior to refactoring. Do not try to refactor a broken application or you risk breaking it further, and end up with a total rewrite.
Refactoring Step 1 Extract Method to move switch statement to its own method.
Refactoring Step 2 Variable names should make sense. (each > rental, thisAmount > result)
Refactoring Step 3 Extract Method amountFor() to Rental class. It uses information from Rental, but no information from Customer. We rename it to more meaningful getCharge().
Refactoring Step 4 Call getCharge() directly from statement() instead of using amountFor() as a middle man method.
Refactoring Step 5 Replace Temp with Query - Cleanup of call to getCharge(). Eliminate temp variables by calling method direct. Less maintenance.
Refactoring Step 6 Extract Method and move frequentRenterPoints to its own method in Rental class where it logically belongs.
Refactoring Step 7 We do Replace Temp with Query for totalAmount. Temp variables can be a problem and really only serve a purpose within their own routine and encourage long, complex routines.
Refactoring Step 8 Also do Replace Temp with Query on frequentRentalPoints to eliminate more temp variables.
Refactoring Step 9 We are now able to create the HTML version of the statement, and we rename the original to represent a Text version.
Refactoring Recap Most refactoring reduces code, while we increased it for this example. We also duplicated a loop to make it happen 4 times instead of once. (getFrequentRenterPoints, getTotalCharge, getTotalFrequentRenterPoints) Optimizing and refactoring are different actions. Refactor first, then optimize later if needed. More refactoring could further clean up Text and HTML statements to DRY it up. (split out header/footer, etc.) Still need more refactoring to allow classification changes by the customer.
Refactoring Step 10 Move Method on getCharge() because it uses data in Movie, so should be in the same class as the data.
Refactoring Step 11 Replace Conditional with Polymorphism on getCharge() because it uses data in Movie, so should be in the same class as the data.
Refactoring Two hats Adding Function Hat Refactoring HatFirst we add functionality, they we refactor it, thenadd more functionality, ...
Refactoring Why Refactor? Without refactoring code decays As code is changed it loses structure, making it harder to see design Regular refactoring preserves design Poorly designed = more code due to duplication Reduced code = easier to maintain Reduced code = easier to understand Helps find bugs Helps us program faster
Refactoring When to Refactor? Should not set aside time to refactor, it is just something we do in short bursts Refactor because you want to do something, and refactoring helps you do it Often a quick refactor can help developing new functionalities move faster Rule of Three → see next slide
Refactoring Rule of Three When you add function Helps to learn code you need to modify Changes code that prevents the addition When you need to fix a bug Make code more understandable Usually highlights the bug During code review Code looks good to developer, but maybe not to the team. More concrete results
Refactoring What do I tell my manager? For a tech saavy manager it may not be hard to explain the benefits Quality centric manager stress quality aspects Perhaps introduce it as a review process There are many resources on Google about value of reviews, inspections, or software development process Schedule driven … Dont tell (controversial?) Find a way to work it into scheduling. Overall it saves time, but some will never “see” it
Refactoring Code “smells” as indicators Bad code “smells” and is a great way of indicating it is time for a refactor. Duplicate Code Long Method Large Class Long Parameter (argument) List Divergent Change - if change is needed in multiple areas to accommodate a change Shotgun Surgery – change causes ripple of other needed changes Feature Envy – a method seems to use another class instead of the one it is in
Refactoring Code “smells” as indicators More “smells” Data Clumps – if data items tend to accompany one another Primitive Obsession Switch statements Parallel Inheritance Hierarchies – caused when you need to create a subclass of one object because you created a subclass of another Lazy Class – classes that do not do much and do not pay for their extra weight Speculative Generality – constructs built for the sake of possibility later
Refactoring Code “smells” as indicators More “smells” Temporary Field Message Chains – object asking for object that asks for another object... Middle Man – directors in place but serving no real purpose Inappropriate Intimacy – classes should not deal too much with each others private parts Data Class – getters and setters, but nothing else Comments – where comments are used as deodorant to cover a bad smell
Refactoring Tests and Refactoring You cannot properly refactor without tests in place. Writing tests is the first step of refactoring, and should happen as you write the code in the first place. (or before, as with TDD)
Refactoring Thank you Adam Culp http://www.geekyboy.com http://github.com/adamculp Twitter @adamculp