60
Refactorización - Refactoring 1 Refactoring Tabla de contenidos Introducción: un ejemplo de refactoring Principios en refactoring Definiciones y orígenes ¿Por qué y cuando refactorizar? Problemas con las refactorizaciones Misceláneo: Herramientas Metodologías ágiles Definición de nuevas refactorizaciones (Big – Low) Conclusiones Bibliografía Fowler, Refactoring: Improving the Design of Existing Code, Addison- Wesley, 1999

Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

  • Upload
    hathien

  • View
    238

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

1

Refactoring

Tabla de contenidos Introducción: un ejemplo de refactoringPrincipios en refactoring

Definiciones y orígenes

¿Por qué y cuando refactorizar?

Problemas con las refactorizaciones

Misceláneo:

Herramientas

Metodologías ágiles

Definición de nuevas refactorizaciones (Big – Low)

Conclusiones

Bibliografía

Fowler, Refactoring: Improving the Design of Existing Code, Addison-

Wesley, 1999

Page 2: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

2

Introducción

DefiniciónRefactoring son una serie de pequeñas transformaciones, cada uno de las cuales cambia la estructura interna del programa sin cambiar su

comportamiento externo.

Verificación del comportamiento externo

Testing – Prueba

Análisis formal de código dirigido por herramientas

Refactoring

Se parte de código existente y con el diseño subyacente en él

Limpia el código para minimizar el coste de los cambios

Mejora el diseño del código una vez que este ha sido escrito

Page 3: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

3

Introducción: un ejemplo de refactoring

Contexto

Calculo e impresión de los alquileres en un Videoclub

El coste del alquiler depende del tiempo de alquiler y tipo de película.

Tipos de películas: regular, children, new-release.

El coste de alquiler también depende de los puntos de frecuencia en el alquiler que dependen del tipo de película.

priceCode: int

Movie

daysRented: int

Rental

statement()

Customer1

1

Page 4: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

4

Class Movie

public class Movie {public static final int CHILDRENS = 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 void setPriceCode(int arg) {_priceCode = arg;

}public String getTitle () {

return _title; }

}

Page 5: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

5

Class Rental

public 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;}

}

Page 6: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

6

Class Customer

public class Customer {private String _name;private Vector _rentals = new Vector();

public Customer (String name) {_name = name;

};public void addRental(Rental arg) {

_rentals.addElement(arg);}public String getName () {

return _name;};public String statement()…

1

Page 7: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

7

Customer.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 lineswitch (each.getMovie().getPriceCode()) {

case Movie.REGULAR:thisAmount += 2;if (each.getDaysRented() > 2)

thisAmount += (each.getDaysRented() - 2) * 1.5;break;

case Movie.NEW_RELEASE:thisAmount += each.getDaysRented() * 3;break;

case Movie.CHILDRENS:thisAmount += 1.5;if (each.getDaysRented() > 3)

thisAmount += (each.getDaysRented() - 3) * 1.5;break;

}…

1

2

Page 8: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

8

Customer.statement()

// add frequent renter pointsfrequentRenterPoints ++;// add bonus for a two day new release rentalif ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&

each.getDaysRented() > 1)frequentRenterPoints ++;//show figures for this rentalresult += "\t" + each.getMovie().getTitle()+ "\t" +

String.valueOf(thisAmount) + "\n";totalAmount += thisAmount;

}//add footer linesresult += "Amount owed is " + String.valueOf(totalAmount) +

"\n";result += "You earned " + String.valueOf(frequentRenterPoints) +

" frequent renter points";return result;

}

2

Page 9: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

9

Customer.statement()

aCustomer aRental aMovie

getMovie

* [for all rentals]

getPriceCode

getDaysRented

statement

Rental Record for Dinsdale Pirhana

Monty Python and the Holy Grail 3.5

Ran 2

Star Trek 27 6

Star Wars 3.2 3

Wallace and Gromit 6

Amount owed is 20.5

You earned 6 frequent renter points

Page 10: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

10

Comentarios

El programa funciona perfectamenteSe observan problemas de diseño

Método excesivamente largo en la clase Client.statement()

Dificultan futuros cambios

Cambios en los requerimientos

Producir una versión de HTML para statement

Solución 1: copy & paste statement -> problemas de código duplicado

La clasificación de películas pronto cambiará, se desconoce la clasificación definitiva

Junto con las reglas para calcular el precio del alquiler y los puntos de alquileres frecuentes

Cuando haya que añadir una nueva característica al programa y el código no mantiene la estructura adecuada para añadir la nueva característica, primero

se refactoriza el programa y luego se añade la nueva característica.

Page 11: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

11

Testear

Construir un conjunto sólido de test

Crear unos clientes

Dar a los clientes unos alquileres de varias clases de películas

Generar manualmente cadena resultantes del alquiler

Comparar las cadenas resultantes con las generadas manualmente

Localiza los posibles fallos (bugs) cuando se refactorizaUsar un simple framework para construir los Test

http://www.junit.org

Comprobar la ejecución correcta de todos los test

Ejecutar el suite de todos los test como parte del proceso de construcción.

Page 12: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

12

Extract Method

Se dispone de un fragmento de código que puede ser agrupado junto. Crear un nuevo método con ese código, cuyo nombre

explique el propósito del método.

void printOwing() {printBanner();//print detailsSystem.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);

}

Page 13: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

13

Extract Method

Crear un método con el nombre que describa la intención del código

Copiar en el nuevo método el código extraídoDeterminar si las variables utilizadas en el código extraído son:

Parámetros

Valores de retorno

Variables locales

CompilarSustituir el fragmento de código con llamadas al nuevo métodoCompilar y testear

Page 14: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

14

Código candidato para extraer

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 lineswitch (each.getMovie().getPriceCode()) {

case Movie.REGULAR:thisAmount += 2;if (each.getDaysRented() > 2)

thisAmount += (each.getDaysRented() - 2) * 1.5;break;

case Movie.NEW_RELEASE:thisAmount += each.getDaysRented() * 3;break;

case Movie.CHILDRENS:thisAmount += 1.5;if (each.getDaysRented() > 3)

thisAmount += (each.getDaysRented() - 3) * 1.5;break;

}…

Page 15: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

15

Aplicación del Extract Method

private int amountFor(Rental each) {int 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_RELEASE:thisAmount += each.getDaysRented() * 3;break;

case Movie.CHILDRENS:thisAmount += 1.5;if (each.getDaysRented() > 3)

thisAmount += (each.getDaysRented() - 3) * 1.5;break;

}return this amount;

}

Page 16: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

16

Statement() después de la extracción

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 linethisAmount=amountFor(each)

}

Page 17: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

17

Fallo de Testeo

private int amountFor(Rental each) {double thisAmount = 0;switch (each.getMovie().getPriceCode()) {

case Movie.REGULAR:

...return this amount;

}

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 linethisAmount=amountFor(each)

…}

La refactorización cambia los programas en pequeños pasos. Si se comete un error es más fácil encontrar el fallo.

Page 18: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

18

Cambiar el nombre de las variables

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_RELEASE:

result += aRental.getDaysRented() * 3;break;

case Movie.CHILDRENS:result += 1.5;if (aRental.getDaysRented() > 3)

result += (aRental.getDaysRented() - 3) * 1.5;break;

}return result;

}

Un buen código debería comunicar claramente que hace, el nombre de las variables es clave para conseguirlo.

Page 19: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

19

Move Method

aMethod()

Class 1

Class 2

Class 1

aMethod()

Class 2

Un método esta usando más características de otra clase que de la clase en la que está definido.

Crear un nuevo método con un cuerpo similar en la clase que más lo usa. Transformar el nuevo método en una delegación o

eliminarlo completamente.

Page 20: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

20

Move Method

Declarar el método en la clase destinoCopiar y ajustar el códigoColocar una referencia del objeto destino en el objeto fuenteTransformar el método original en un método delegado

amountOf(Rental each) {return each.charge()}

Compilar y testearEncontrar todos los usuarios de ese método

Transformarles para llamar al método sobre la clase destino

Eliminar el método original Compilar y testear

Page 21: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

21

Aplicación Move Method Rental.getCharge()

//class Rentaldouble getCharge() {

double result = 0;switch (getMovie().getPriceCode()) {case Movie.REGULAR:

result += 2;if (getDaysRented() > 2)

result += (getDaysRented() - 2) * 1.5;break;

case Movie.NEW_RELEASE:result += getDaysRented() * 3;break;

case Movie.CHILDRENS:result += 1.5;if (getDaysRented() > 3)

result += (getDaysRented() - 3) * 1.5;break;

}return result;

} 1 statement()

Customer

getCharge()

daysRented: int

Rental

priceCode: int

Movie

Page 22: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

22

Aplicación Move Method Customer.amountFor()

//class Customerpublic 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 linethisAmount=each.getCharge();

…}private double amountFor(Rental rental) {return rental.charge();

}

Si el método movido es privado no tiene sentido mantener el delegado por que no van a existir clientes del mismo.

Page 23: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

23

Problemas con variables temporales//class Customerpublic 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 = each.getCharge();// add frequent renter pointfrequentRenterPoints ++;// add bonus for a two day new release rentalif ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&

each.getDaysRented() > 1) frequentRenterPoints ++;

//show figures for this rentalresult += "\t" + each.getMovie().getTitle()+ "\t" +

String.valueOf(thisAmount) + "\n“;totalAmount += thisAmount;

}//add footer linesresult += "Amount owed is " + String.valueOf(totalAmount) + "\n";result += "You earned " + String.valueOf(frequentRenterPoints) +

" frequent renter points“;return result;

}

Page 24: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

24

Problemas con variables temporales

Ámbito temporal y local

Aumentan complejidad - > aumentan tamaño de los métodos

Pueden provocar duplicación de código

Dificultan la trazabilidad del códigoSu eliminación tiene un coste de rendimiento

Gran parte del tiempo de ejecución esta localizado en pequeñas porciones de código

Fácil localizar esos fragmentos de código

La mejor forma para optimizar es primero escribir un programa bien factorizado y luego optimizar

Page 25: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

25

Replace Temp with Query

Se esta utilizando una variable temporal para manejar el resultado de una expresión.

Extraer la expresión en un método. Sustituir todas las referencias de la variable por la invocación al método. El nuevo método puede ser luego

utilizado en otros métodos.

double basePrice = _quantity * _itemPrice;if (basePrice > 1000)

return basePrice * 0.95;else

return basePrice * 0.98;

if (basePrice() > 1000)return basePrice() * 0.95;

elsereturn basePrice() * 0.98;

...double basePrice() {

return _quantity * _itemPrice;}

Page 26: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

26

Replace temp with Query

Encontrar las variables temporales en las sentencias de asignación que son asignadas una única vez.

Java -> final y compilar

Extraer el lado derecho de la asignación en un nuevo método

A priori método como privado

Asegurarse que no se modifica ningún objeto

Reemplazar todas las referencias de la variable temporal por el nuevo método

Eliminar la declaración y la sentencia de asignación de la variable temporal

Compilar y testear

Page 27: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

27

Aplicación Replace Temp with Query Customer.statement()

//class Customerpublic String statement() {

double totalAmount = 0;int frequentRenterPoints = 0;Enumeration rentals = _rentals.elements();String result = "Rental Record for " + getName() + "\n“; while (rentals.hasMoreElements()) {

Rental each = (Rental) rentals.nextElement();// add frequent renter pointfrequentRenterPoints ++;// add bonus for a two day new release rentalif ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&

each.getDaysRented() > 1) frequentRenterPoints ++;

//show figures for this rentalresult += "\t" + each.getMovie().getTitle()+ "\t" +

String.valueOf(each.getCharge();) + "\n“;totalAmount += each.getCharge();;

}//add footer linesresult += "Amount owed is " + String.valueOf(totalAmount) + "\n";result += "You earned " + String.valueOf(frequentRenterPoints) +

" frequent renter points“;return result;

}

Page 28: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

28

Aplicación Extract y Move Method Rental.getFrequentRenterPoints()

//class Customerpublic String statement() {

double totalAmount = 0;int frequentRenterPoints = 0;Enumeration rentals = _rentals.elements();String result = "Rental Record for " + getName() + "\n";while (rentals.hasMoreElements()) {Rental each = (Rental) rentals.nextElement();frequentRenterPoints += each.getFrequentRenterPoints();//show figures for this rentalresult += "\t" + each.getMovie().getTitle()+ "\t" +

String.valueOf(each.getCharge()) + "\n";totalAmount += each.getCharge();

}//add footer linesresult += "Amount owed is " + String.valueOf(totalAmount) + "\n";result += "You earned " +String.valueOf(frequentRenterPoints) + "

frequent renter points";return result;

}

Page 29: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

29

UML: Después de refactorizar

1statement()

Customer

getCharge()getFrequentRenterPoints()

daysRented: int

Rental

priceCode: int

Movie

aCustomer aRental aMovie

getCharge

* [for all rentals]

getPriceCode

statement

getFrequentRenterPointsgetPriceCode

Page 30: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

30

Eliminación variables temporales totalAmount - frequentRenterPoints

//class Customerpublic String statement() {double totalAmount = 0;int frequentRenterPoints = 0;Enumeration rentals = _rentals.elements();String result = "Rental Record for " + getName() + "\n";while (rentals.hasMoreElements()) {Rental each = (Rental) rentals.nextElement();frequentRenterPoints += each.getFrequentRenterPoints();//show figures for this rentalresult += "\t" + each.getMovie().getTitle()+ "\t" +

String.valueOf(each.getCharge()) + "\n";totalAmount += each.getCharge();

}//add footer linesresult += "Amount owed is " + String.valueOf(totalAmount) + "\n";result += "You earned " +String.valueOf(frequentRenterPoints) +

"frequent renter points";return result;

}

Page 31: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

31

Aplicación Replace Temp with Query Nuevos métodos: Customer.getTotalCharge(),

Customer.getTotalFrequentRenterPoints()

//class Customer…private double getTotalCharge() {double result = 0;Enumeration rentals = _rentals.elements();while (rentals.hasMoreElements()) {

Rental each = (Rental) rentals.nextElement();result += each.getCharge();

}return result;

}

private int getTotalFrequentRenterPoints(){int result = 0;Enumeration rentals = _rentals.elements();while (rentals.hasMoreElements()) {

Rental each = (Rental) rentals.nextElement();result += each.getFrequentRenterPoints();

}return result;

}

Page 32: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

32

Aplicación Replace Temp with Query Customer.statement( )

//Customerpublic String statement() {//Remove declarationsEnumeration rentals = _rentals.elements();String result = "Rental Record for " + getName() + "\n";while (rentals.hasMoreElements()) {

Rental each = (Rental) rentals.nextElement();//show figures for this rentalresult += "\t" + each.getMovie().getTitle()+ "\t" +

String.valueOf(each.getCharge()) + "\n";}//add footer linesresult += "Amount owed is " + String.valueOf(getTotalCharge()) +

"\n";result += "You earned " +

String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points";

return result;}

Page 33: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

33

UML: Después de refactorizar

1statement()getTotalCharge()getTotalFrequentRenterPoints()

Customer

getCharge()getFrequentRenterPoints()

daysRented: int

Rental

priceCode: int

Movie

aCustomer aRental aMovie

* [for all rentals] getCharge

getTotalCharge

getPriceCode

statement

* [for all rentals] getFrequentRenterPoints

getPriceCode

getTotalFrequentRenterPoints

Page 34: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

34

Añadir funcionalidad Customer.htmlStatement()

//Customerpublic String htmlStatement() {Enumeration rentals = _rentals.elements();String result = "<H1>Rentals for <EM>" + getName() +

"</EM></H1><P>\n";while (rentals.hasMoreElements()) {

Rental each = (Rental) rentals.nextElement();//show figures for each rentalresult += each.getMovie().getTitle()+ ": " +

String.valueOf(each.getCharge()) + "<BR>\n";}//add footer linesresult += "<P>You owe <EM>" +

String.valueOf(getTotalCharge())+ "</EM><P>\n";result += "On this rental you earned <EM>" +

String.valueOf(getTotalFrequentRenterPoints()) +"</EM> frequent renter points<P>";

return result;}

Page 35: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

35

Clasificación desconocida de películas

//class Rentaldouble getCharge() {double result = 0;switch (getMovie().getPriceCode()) {

case Movie.REGULAR:result += 2;if (getDaysRented() > 2)

result += (getDaysRented() - 2) * 1.5;break;

case Movie.NEW_RELEASE:result += getDaysRented() * 3;break;

case Movie.CHILDRENS:result += 1.5;if (getDaysRented() > 3)

result += (getDaysRented() - 3) * 1.5;break;

}return result;

}

Page 36: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

36

Aplicación Move Method Movie.getCharge()

//class Rentaldouble getCharge(){

return _movie.getCharge(_daysRented);}//class Moviedouble getCharge(int daysRented) {

double result = 0;switch (getPriceCode()) {

case Movie.REGULAR:result += 2;if (daysRented > 2)

result += (daysRented - 2) * 1.5;break;

case Movie.NEW_RELEASE:result += daysRented * 3;break;

case Movie.CHILDRENS:result += 1.5;if (daysRented > 3)

result += (daysRented - 3) * 1.5;break;

}return result;

}

Hacer lo mismo con Rental.frequentRenterPoints()

Page 37: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

37

Clasificación por herencia

Permite reemplazar las sentencias switch por polimorfismoProblema

Un película puede cambiar su clasificación durante su vida

Un objeto no puede cambiar de clase durante su vida

Solución aplicar el patrón de diseño Estado (State)

getCharge

Movie

getCharge

Regular Movie

getCharge

Childrens Movie

getCharge

New Release Movie

getCharge

Price

getCharge

Regular Price

getCharge

Childrens Price

getCharge

New Release Price

getCharge

Movie

1

return price.getCharge

Page 38: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

38

Replace Type Code with State/Strategy

ENGINEER : intSALESMAN : intty pe : int

Employee Employee Type

Engineer Salesman

Employee1

Se dispone para clasificar de un código-tipo que afecta al comportamiento de una clase, pero no puede usarse

clasificación por herencia.Replace the type code with a state object.

Page 39: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

39

Replace Type Code with State/Strategy

Crear una nueva clase para el código - tipoAñadir subclases del objeto estado, una por cada código - tipoCrear un método abstracto en la superclase que retorne el

código-tipo. Definir en las subclases dicho método, retornando el codigo – tipo correcto.

CompilarCrear un campo en la clase original para referenciar al objeto

estado.Cambiar los métodos de la clase original para la obtención del

código – tipo por una delegación sobre el objeto estado.Cambiar el codigo – tipo poniendo métodos para asignar una

instancia de una subclaseCompilar y testear.

Page 40: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

40

Transformar código – precio en jerarquía de precios

abstract class Price {abstract int getPriceCode();

}

class ChildrensPrice extends Price {int getPriceCode() {

return Movie.CHILDRENS;}

}

class NewReleasePrice extends Price {int getPriceCode() {

return Movie.NEW_RELEASE;}

}

class RegularPrice extends Price {int getPriceCode() {

return Movie.REGULAR;}

}

Page 41: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

41

Cambiar metodos de acceso en Movie

//Class Moviepublic int getPriceCode() {

return _price.getPriceCode();}public void setPriceCode(int arg) {

switch (arg) {case REGULAR:

_price = new RegularPrice();break;

case CHILDRENS:_price = new ChildrensPrice();break;

case NEW_RELEASE:_price = new NewReleasePrice();break;

default:throw new IllegalArgumentException("Incor rect Price Code");

}}private Price _price;

//Class Moviepublic int getPriceCode() {

return _priceCode;}public setPriceCode (int arg){

_priceCode = arg;}private int _priceCode;

Page 42: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

42

Replace Conditional With Polymorphism

getSpeed

Bird

getSpeed

European

getSpeed

African

getSpeed

Norweigian Blue

Se tiene una sentencia condicional que selecciona diferentes comportamientos dependiendo del tipo de objeto.

Mover cada rama condicional a un método redefinido en una subclase. Transformar el método original a abstract.

double getSpeed() {switch (_type) {case EUROPEAN:

return getBaseSpeed();case AFRICAN:

return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;case NORWEIGIAN_BLUE:

return (_isNailed) ? 0 : getBaseSpeed(_voltage);}throw new RuntimeException ("Should be unreachable");

}

Page 43: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

43

Replace Conditional with Polymorphism

Mover la sentencia condicional a la superclase de la jerarquía de herencia

Copiar el cuerpo de cada sentencia condicional como el cuerpo del método redefinido en una subclase

Compilar y testearRepetir el mismo proceso para cada rama condicionalSustituir las sentencias condicionales por un método abstracto

Page 44: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

44

Aplicación Move Method Movie.getCharge() a Price. getCharge()

//class Movie…double getCharge(int daysRented) {

return _price.getCharge(daysRented);}//class Price…double getCharge(int daysRented) {

double result = 0;switch (getPriceCode()) {case Movie.REGULAR:

result += 2;if (daysRented > 2)result += (daysRented - 2) * 1.5;

break;case Movie.NEW_RELEASE:

result += daysRented * 3;break;

case Movie.CHILDRENS:result += 1.5;if (daysRented > 3)

result += (daysRented - 3) * 1.5;break;

}return result;

}

Page 45: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

45

Replace Conditional with Polymorphism Price.getCharge( )

//Class RegularPricedouble getCharge(int daysRented){double result = 2;if (daysRented > 2)

result += (daysRented - 2) * 1.5;return result;

}//Class ChildrensPrice

double getCharge(int daysRented){double result = 1.5;if (daysRented > 3)

result += (daysRented - 3) * 1.5;return result;

}//Class NewReleasePrice

double getCharge(int daysRented){return daysRented * 3;

}

//Class Priceabstract double getCharge(int daysRented);

Hacer lo mismo con Rental.getFrequentRenterPoints()

Page 46: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

46

Resumen…

En el ejemplo se mejoro el diseño de un programa para añadir funcionalidad

Más fácil añadir nuevos servicios a los clientes

Más fácil añadir nuevos tipos de películas

Dismuye depuración/debugging durante el proceso de refactorización

Las transformaciones controladas reducen la probabilidad de fallos

Las pequeñas transformaciones hacen que los fallos sean más fáciles de encontrar

En el ejemplo se ilustran varias refactorizaciones

Extract Method

Move Method

Replace Temp with Query

Replace Type Code with State/Strategy

Replace Switch with Polymorphism

Mirar el catálogo de Fowler en: http://www.refactoring.com/

Page 47: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

47

Tabla de contenidos

Introducción: un ejemplo de refactoring

Principios en refactoringDefiniciones y orígenes ¿Por qué y cuando refactorizar? Problemas con las refactorizaciones

Misceláneo:

Herramientas

Metodologías ágiles

Definición de nuevas refactorizaciones (Big – Low)

Conclusiones

Bibliografía

Fowler, Refactoring: Improving the Design of Existing Code, Addison-

Wesley, 1999

Page 48: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

48

Definiciones y orígenes de Refactoring

Uso relajado

Reorganizar un programa o algo

Como nombre

Cambio en la estructura interna de un programa para facilitar su comprensión y abaratar sus modificaciones, sin modificar el comportamiento externo

Como verbo

Actividad de reestructurar el software aplicando una serie d e refactoring/transformaciones sin cambiar el comportamiento externo del software.

Origenes

Ward Cunningham y Kent Beck (Smalltalk)

Ralph Johnson y Bill Opdyke (Definición formal d)

ftp://st.cs.uiuc.edu/pub/papers/refactoring/opdyke-thesis.ps.Z

John Brant y Don Roberts (Refactoring Browser)

Page 49: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

49

¿Cuando refactorizar?

Añadir nueva funcionalidad

Refactorizar el código existente hasta que se entienda

Refactorizar el diseño para facilitar la incorporación de la nueva funcionalidad

Para encontrar fallos

Refactorizar para entender el código

Para revisiones de código

Efecto inmediato de una revisión de código

Regla de tres para hacer una cosa

(1ª vez) Hazla (2ª vez) Duplica (3ª vez) Refactoriza

El tiempo necesario para refactorizar hay que incluirlo como una actividad normal en el desarrollo del software.

Page 50: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

50

Metáfora de los dos sombreros

Añadir funcionalidad

No modifica el código existenteAñade nueva funcionalidad al

sistemaAñade nuevos testObtener los resultados de test

Refactorizar

Modifica el código existenteNo añade nuevas característicasNo añade test (pero podría modificar

alguno)

Cambio de interfaz

Reestructura el código existente para eliminar redundancia

Realizar intercambios de sombreros, pero llevar puesto sólo uno cada vez

Page 51: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

51

¿Por qué refactorizar?

Para mejorar el diseño del software

Combate la perdida de la estructura de código (diseño) en cambios acumulativos

Facilita futuros cambios en el programa

Hacer el software más fácil de entender

Escribir para que lo entienda las personas, no el compilador

Entender código desarrollado por otros

Ayuda a encontrar errores

Refactorizar mientras se depura para clarificar el código

www.junit.org

www.xprogramming.com/software

Refactorizar ayuda a programar más rápido

Page 52: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

52

Problemas con la refactorización

Convencer a directivos

Directivos motivados por la calidad -> mejora la calidad del proceso

Otros -> Aumento de velocidad de desarrollo

Migraciones de bases de datos

Aislar la estructura persistente de la base de datos de los objetos

Añadir flexibilidad -> aísla los cambios de los distintos módulos a costa de añadir complejidad a la estructura

Interfaces publicadas -> más allá del modificador public

Si una refactorización cambia la signatura de un método, no existe problema si se tiene acceso a todas las llamadas a ese método

Publicar sólo cuando sea necesario

Un cambio obliga a mantener la antigua interface -> problema de códigos legado

Publicar dentro del equipo de desarrollo

Trabajar sin casos de prueba definidos

Para refactorizar el código debe funcionar correctamente sino reescribirlo

Page 53: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

53

Tabla de contenidos

Introducción: un ejemplo de refactoringPrincipios en refactoring

Definiciones y orígenes

¿Por qué y cuando refactorizar?

Problemas con las refactorizaciones

Misceláneo:HerramientasMetodologías ágiles

Definición de nuevas refactorizaciones (Big – Low)Conclusiones

Bibliografía

Fowler, Refactoring: Improving the Design of Existing Code, Addison-

Wesley, 1999

Page 54: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

54

Herramientas de Refactoring

Basadas en transformaciones demostrables

Repositorio para consultar información

Entidades del programa: clases, métodos, instrucciones,

Encontrar todos los métodos que leen o escriben una var. de instancia

Construir parses de programas

Probar matemáticamente que la refactorización no cambia la semántica

Incorporar las refactorizaciones en entornos integrados de desarrolloAumentan la velocidad en el proceso de refactorización

Operaciones para realizar Extract method con una herramienta

Seleccionar código a extraer

Escribir el código del método

No son ciencia ficción - > falta madurar

Page 55: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

55

Herramientas de Refactoring

Page 56: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

56

La importancia de los Test

Incluso con la herramienta, las pruebas son importantes

No se pueden probar todas las refactorizaciones

Es tan importante codificar los test cómo escribir el código

Para preservar el comportamiento en los cambios

Los test deben comprobarse automáticamente (self-checking)

Incluir los resultados esperados dentro del propio test

retornar “OK” si los resultados obtenido = resultados esperado

Ejecutar un conjunto de Test (suite) con un único comandoTestear con cada compilaciónDirecciones de interés

Page 57: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

57

Decisiones de diseño

Diseño planeado

Considera necesidades actuales y posibles necesidades futuras

Se diseña para minimizar los cambios con futuras necesidades

Añade un parche en el código si aparece alguna necesidad imprevista

Diseño evolutivo

Considerar necesidades actuales y posibles necesidades futuras

Evaluar el coste de la actual flexibilidad versus coste de refactorizar más tarde

Refactorizar cuando el cambio aparece

XP

Metodología desarrollada por Kent Beck

Diseñada para soportar los cambios

Prácticas claves

Desarrollo iterativo

Código Self Testing

Refactoring

Programación por parejas

Influencia de las refactorizaciones para dar soporte el diseño evolutivo

©

Page 58: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

58

Técnicas de equipo

Animar a la cultura de la refactorización

Nadie hace bien algo la primera vez

Nadie puede escribir código limpio sin revisiones

Refactorizar es un paso al progreso

Proporcionar un buen conjunto de Test

Los test son esenciales para refactorizar

Construir el software y ejecutar los test de forma diaria

Programación por parejas

Dos programadores trabajando juntos pueden ser más rápidos que trabajando separadamente

Refactorizar con escritor de la clase y con un usuario de la misma

Page 59: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

59

Crear refactorizaciones propias

Considerar un cambio en el programaDebería cambiar el comportamiento externo del programaDesglosar el cambio en pequeños pasos

Localizar los puntos donde compilar y testear

Realizar los cambio -> anotar que es lo que se hace

Utilizar plantillas para definir los cambios

Si ocurre algún problema, considerar cómo eliminarlo en el futuro

Realizar los cambios de nuevo, comprobar y redefinir las anotaciones

Después de dos o tres veces de ejecutar la refactorización se dispondrá de una nueva refactorización

Page 60: Refactorización - Refactoringmaterias.fi.uba.ar/7510/practica/zips/Refactoring.pdf · Refactorización - Refactoring 1. Refactoring. Tabla de contenidos Introducción: un ejemplo

Refactorización - Refactoring

60

Conclusiones

El principal beneficio de los objetos es su facilidad para ser cambiados. (Encapsulamiento)

Las refactorizaciones permiten mejorar el diseño una vez que este ha sido escrito.

El tema de Refactoring esta inmaduro:

Falta de documentación escrita

Falta y mejora de herramientas