A (Very) Simple Example Consolidate duplicate conditional fragments if (isSpecialDeal()) { total = price * 0.95; send (); } else { total = price * 0.98;

Slides:



Advertisements
Similar presentations
12-Dec-14 Refactoring IV. Previously discussed bad smells Duplicated code — and other forms of redundancy Long method — use short methods that delegate.
Advertisements

About Me – Frank Xu Education ▫ North Dakota State University  Ph.D. in Software Engineering ▫ Towson University  MS in Computer Science ▫ Southeast.
© 2010 Shawn A. Bohner Software Construction and Evolution - CSSE 375 Even more Bad Smells in Code Shawn & Steve Q1 Shawn & Steve Hint 
Software Testing and Maintenance 1 Today’s Agenda  Course Evaluation  HW 4 Return  HW 5 Correction  Quiz 4 Next Class  Software Refactoring.
Refactoring: Improving the Design of Existing Code © Martin Fowler, Martin Fowler.
Refactoring Overview  What is refactoring?  What are four good reasons to refactor?  When should you refactor?  What is a bad smell (relative to refactoring.
Software Construction and Evolution - CSSE 375 Bad Smells in Code Shawn Bohner & Steve Chenoweth.
1 Software Maintenance and Evolution CSSE 575: Session 1, Part 4 Even more Bad Smells in Code Steve Chenoweth Office Phone: (812) Cell: (937)
Refactoring Software Engineering Refactoring Software Engineering 2011 Department of Computer Science Ben-Gurion university Based on slides of: Mira Balaban.
Introduction to Refactoring Excerpted from ‘What is Refactoring?’ by William C. Wake and Refactoring: Improving the Design of Existing Code by Martin Fowler.
George Blank University Lecturer. REFACTORING Improving the Design of Existing Code Supplement to Ian Sommerville, Software Engineering, Chapter 20 Prepared.
25-Jun-15 Refactoring III. General philosophy A refactoring is just a way of rearranging code Refactorings are used to solve problems If there’s no problem,
REFACTORING Improving the Design of Existing Code Atakan Şimşek e
Refactoring, continue. Announcements Due today Resubmit HW5 (feedback on HW5 in SVN) HW7 HW8 will be up tonight Due Saturday at 5:59! Quiz 9 Spring 15.
Test. A software house decides to develop a DVD renting program. The product manager identifies the following requirements: Every DVD will have a title,
Maintenance Refactoring and Code Smells. Where are we? Over the semester we have talked about Software Engineering. The overall goal of software engineering.
Refactoring: Improving the Design of Existing Code © Martin Fowler, Martin Fowler.
Refactoring - A disciplined approach to rework for better design.
Refactoring Improving the structure of existing code Refactoring1.
Refactoring (continued) Source: "Refactoring: Improving the Design of Existing Code", Martin Fowler.
Refactoring1 Improving the structure of existing code.
Refactoring Software Engineering Refactoring Software Engineering 2012 Department of Computer Science Ben-Gurion university Based on slides of: Mira Balaban.
CS4723 Software Engineering Lecture 12 Software Design Quality.
Agile Software Development Lab Spring 2008 R O O T S 1 Sina Thomas ) XP: Extreme.
Introduction to Refactoring Jim Cooper Falafel Software.
Informatics 122 Software Design II
Refactoring: Improving the Design of Existing Code © Martin Fowler, Martin Fowler.
Incremental Design Why incremental design? Goal of incremental design Tools for incremental design  UML diagrams  Design principles  Design patterns.
Software Engineering CS3003 Lecture 4 Code bad smells and refactoring.
Refactoring: Code Smells. Admin Notes REGISTER FOR BLACKBOARD Watch blackboard site for updates on class as hurricane season approaches.
Refactoring. Refactoring Overview  What is refactoring?  What are four good reasons to refactor?  When should you refactor?  What is a bad smell (relative.
Today’s Agenda  More refactoring patterns Software Testing and Maintenance 1.
Refactoring 2. Admin Blackboard Quiz Acknowledgements Material in this presentation was drawn from Martin Fowler, Refactoring: Improving the Design of.
REFACTORINGREFACTORING. Realities Code evolves substantially during development Requirements changes 1%-4% per month on a project Current methodologies.
NJIT 1 Test Driven Development and Refactoring Larman, Chapter 21.
Refactoring II Dealing with Polymorphism. Switch in Rental Switches on Movie! class Rental … public double getCharge() { double result = 0; switch (getMovie().getPriceCode()){
Module 3. Smells Between Classes Course: Refactoring.
1 Software Maintenance and Evolution CSSE 575: Session 2, Part 1 Refactoring Principles Steve Chenoweth Office Phone: (812) Cell: (937)
Design Patterns.
Software Construction and Evolution - CSSE 375 Making Method Calls Simpler Shawn and Steve Below – “Be the character!” The late acting teacher Lee Strasberg.
1 Software Maintenance and Evolution CSSE 575: Session 3, Part 3 Dealing with Generalization Steve Chenoweth Office Phone: (812) Cell: (937)
Refactoring. Announcements HW7 due today, HW8 coming up tomorrow (I’m taking a late day on posting HW8) Grades and feedback for HW0-5 in Homework Server.
Refactoring1 Improving the structure of existing code.
Pertemuan 12 Refactoring Mata kuliah: T0144 – Advanced Topics in Software Engineering Tahun: 2010.
CSSE 375 Organizing Data – Part 1 Shawn and Steve Q1.
Software Construction and Evolution - CSSE 375 Dealing with Generalization Steve and Shawn Left – In the 1990 movie “The Freshman,” Matthew Broderick,
Refactoring Improving code after it has been written.
Catalog of Refactoring (1) Composing Methods. Code Smells Long methods Dubious temporary variables Dubious methods.
ICONFINDER ICONFINDER Founded Django based web application -PostgreSQL -Elasticsearch -Amazon Elastic Compute.
Refactoring (1). Software Evolution Cope with change Feature bloat Design decay Code duplications “Pattern time is refactoring time” Make future changes.
Principles and examples
Steve Chenoweth Office Phone: (812) Cell: (937)
Outline A. What is Refactoring? B. Why do we Refactor?
Steve Chenoweth Office Phone: (812) Cell: (937)
Refactoring and Code Smells
Software Construction and Evolution - CSSE 375 Composing Methods
بازآرایی برنامه Code Refactoring
Refactoring III 27-Nov-18.
Refactoring.
Reusability 11/29/2018© 2006 ITT Educational Services Inc.
Code Smells 1.
private double amountFor(Rental aRental)
Improving the structure of existing code
Refactoring and Code Smells
Refactoring III 25-Dec-18.
Refactoring.
Refactoring and Code Smells
Refactoring.
Refactoring and Code Smells
Presentation transcript:

A (Very) Simple Example Consolidate duplicate conditional fragments if (isSpecialDeal()) { total = price * 0.95; send (); } else { total = price * 0.98; send (); } if (isSpecialDeal()) total = price * 0.95; else total = price * 0.98; send ();

Software Engineering II Lecture 42 Fakhar Lodhi

Recap

A (Very) Simple Example Better Still if (isSpecialDeal()) total = price * 0.95; else total = price * 0.98; send (); if (isSpecialDeal()) factor = 0.95; else factor = 0.98; total = price * factor; send ();

Refactoring: Where to Start? How do you identify code that needs to be refactored? –“Bad Smells” in Code

Bad Smells in Code 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

Bad Smells in Code Large Class Long Parameter List Divergent Change Shotgun Surgery Feature Envy Data Clumps Primitive Obsession

Bad Smells in Code Switch Statements Parallel Inheritance Hierarchies Lazy Class Speculative Generality Temporary Field Message Chains Middle Man

Bad Smells in Code Inappropriate Intimacy Alternative Classes with Different Interfaces Incomplete Library Class Data Class Refused Bequest Comments (!)

Refactoring Example A simple program for a video store –Movie –Rental –Customer Program is told which movies a customer rented and for how long and then, it calculates: –The charges –Frequent renter points Customer object can also print a statement (in ASCII)

Initial Class Diagram statement() works by looping through all rentals For each rental, it retrieves its movie and the number of days it was rented; it also retrieves the price code of the movie. It then calculates the price for each movie rental and the number of frequent renter points and returns the generated statement as a string; Movie priceCode: int Rental daysRented: int Customer statement() 1 ** 1

public class Movie { public static final int CHILDREN = 2; public static final int REGULAR = 0; public static final int NEW_RELEASE = 1; private String _title; private int _priceCode; public Movie(String title, int priceCode) { _title = title; _priceCode = priceCode } public int getPriceCode() { return _priceCode; } public String getTitle() { return _title; } public void setPriceCode(int priceCode) { _priceCode = priceCode; }

class Rental { private Movie _movie; private int _daysRented; public Rental(Movie movie, int daysRented) { _movie = movie; _daysRented = daysRented; } public int getdaysRented() { return _daysRented; } public Movie getMovie() { return _movie; } }

class Customer { private String _name; private Vector _rentals = new Vector(); public Customer(String name) { _name = name; } public String getName() { return _name; } public void addRental (Rental arg) { _rentals.addElement(arg); } public String statement(); }

public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental record for ” + getName() + “\n”; while (rentals.hasMoreElements()) { double thisAmount = 0; Rental each = (Rental) rentals.nextElement(); // determine amounts for each line

switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmount += 2; if (each.getDaysRented() > 2) thisAmount += (each.getDaysRented() - 2) *1.5; break; case Movie.NEW_RELAESE: thisAmount += each.getDaysRented() * 3; break; case Movie.CHILDREN: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) *1.5; break; }

// add frequent renter points frequentRenterPoints++; // add bonus for two day new release rental if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints++; // show figures for this rental result += “\t” + each.getMovie().getTitle() + “\t” + String.valueOf(thisAmount) + “\n”; totalAmount += thisAmount; } // add footer lines result += “Amount owed is ” + String.valueOf(totalAmount) + “\n”; result += “You earned ” + String.valueOf(frequentRenterPoints) + “\n”; return result; }

How to start? We want to decompose statement() into smaller pieces which are easier to manage and move around. We’ll start with “Extract Method” –target the switch statement first

Extract Method You have a code fragment that can be grouped together Turn the fragment into a method whose name explains the purpose of the fragment

switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmount += 2; if (each.getDaysRented() > 2) thisAmount += (each.getDaysRented() - 2) *1.5; break; case Movie.NEW_RELAESE: thisAmount += each.getDaysRented() * 3; break; case Movie.CHILDREN: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) *1.5; break; } Extract method Watch for local variables ‘each’ and ‘thisAmount’ non-modifiable variables can be passed as parameter to new method (if required) modified variables require more care; since there is only one (i.e. thisAmount), we can make it the return value of the new method. each does not change but thisAmount does so each is a parameter and value is returned to thisAmount

private double amountFor(Rental each) double thisAmount = 0; switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmount += 2; if (each.getDaysRented() > 2) thisAmount += (each.getDaysRented() - 2) *1.5; break; case Movie.NEW_RELAESE: thisAmount += each.getDaysRented() * 3; break; case Movie.CHILDREN: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) *1.5; break; } return thisAmount; } Switch statement is extracted and put in a new method “amountFor()” value of ‘thisAmount’ is returned variable ‘each’ is passed as a parameter

Be careful! Pitfalls –be careful about return types; in the original statement(), thisAmount is a double, but it would be easy to make a mistake of having the new method return an int. This will cause rounding-off error. Always remember to test after each change.

private double amountFor(Rental each) double thisAmount = 0; switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmount += 2; if (each.getDaysRented() > 2) thisAmount += (each.getDaysRented() - 2) *1.5; break; case Movie.NEW_RELAESE: thisAmount += each.getDaysRented() * 3; break; case Movie.CHILDREN: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) *1.5; break; } return thisAmount; } look at the variable names and use more suitable ones

private double amountFor(Rental each) double thisAmount = 0; switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmount += 2; if (each.getDaysRented() > 2) thisAmount += (each.getDaysRented() - 2) *1.5; break; case Movie.NEW_RELAESE: thisAmount += each.getDaysRented() * 3; break; case Movie.CHILDREN: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) *1.5; break; } return thisAmount; } each and thisAmount

private double amountFor(Rental aRental) double result = 0; switch (aRental.getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (aRental.getDaysRented() > 2) result += (aRental.getDaysRented() - 2) *1.5; break; case Movie.NEW_RELAESE: result += aRental.getDaysRented() * 3; break; case Movie.CHILDREN: result += 1.5; if (aRental.getDaysRented() > 3) result += (aRental.getDaysRented() - 3) *1.5; break; } return result; } each  aRental thisAmount  result

public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental record for ” + getName() + “\n”; while (rentals.hasMoreElements()) { double thisAmount = 0; Rental each = (Rental) rentals.nextElement(); thisAmount = amountFor(each); // the switch statement has been replaced by this // call to amountFor … the rest continues as before

private double amountFor(Rental aRental) double result = 0; switch (aRental.getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (aRental.getDaysRented() > 2) result += (aRental.getDaysRented() - 2) *1.5; break; case Movie.NEW_RELAESE: result += aRental.getDaysRented() * 3; break; case Movie.CHILDREN: result += 1.5; if (aRental.getDaysRented() > 3) result += (aRental.getDaysRented() - 3) *1.5; break; } return result; } amountFor() uses information from the Rental class, but it does not use information from the Customer class where it is currently located. amountFor() should be part of the Rental class and not the customer class, so it should be moved to the Rental class

Move method Methods should be located close to the data they operate. –we can get rid of the parameters this way. –let’s also rename the method to getCharge() so as to clarify what it is doing. –as a result, back in customer, we must delete the old method and change the call from amountFor(each) to each.getCharge() compile and test

private double amountFor(Rental aRental) return aRental.getCharge(); } initially replace the body with the call while (rentals.hasMoreElements()) { double thisAmount = 0; Rental each = (Rental) rentals.nextElement(); thisAmount = each.getCharge(); … the rest continues as before remove this method later on and call directly Call to amountFor has been replaced and getCharge() is called directly

New Class Diagram No major changes; however, Customer is now a smaller class and an operation has been moved to the class that has the data it needs to do the job Movie priceCode: int Rental daysRented: int getCharge() Customer statement() 1 ** 1