Está en la página 1de 60

Refactorizacin - Refactoring

Refactoring
Tabla de contenidos Introduccin: un ejemplo de refactoring Principios en refactoring
Definiciones y orgenes Por qu y cuando refactorizar? Problemas con las refactorizaciones

Miscelneo:
Herramientas Metodologas giles Definicin de nuevas refactorizaciones (Big Low) Conclusiones Fowler, Refactoring: Improving the Design of Existing Code, AddisonWesley, 1999
1

Bibliografa

Refactorizacin - Refactoring

Introduccin
Definicin
Refactoring son una serie de pequeas transformaciones, cada uno de las cuales cambia la estructura interna del programa sin cambiar su comportamiento externo.

Verificacin del comportamiento externo


Testing Prueba Anlisis formal de cdigo dirigido por herramientas

Refactoring
Se parte de cdigo existente y con el diseo subyacente en l Limpia el cdigo para minimizar el coste de los cambios Mejora el diseo del cdigo una vez que este ha sido escrito

Refactorizacin - Refactoring

Introduccin: un ejemplo de refactoring


Contexto
Calculo e impresin de los alquileres en un Videoclub El coste del alquiler depende del tiempo de alquiler y tipo de pelcula. Tipos de pelculas: regular, children, new-release. El coste de alquiler tambin depende de los puntos de frecuencia en el alquiler que dependen del tipo de pelcula.

Movie priceCode: int

Rental daysRented: int

Customer statement()

Refactorizacin - Refactoring

Class Movie
public class Movie { public static final int public static final int public static final int private String _title; private int _priceCode; public Movie(String title, int priceCode) { _title = title; _priceCode = priceCode;
}

CHILDRENS = 2; REGULAR = 0; NEW_RELEASE = 1;

public int getPriceCode() { return _priceCode;


}

public void setPriceCode(int arg) { _priceCode = arg;


}

public String getTitle () { return _title;


} }

Refactorizacin - Refactoring

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

Refactorizacin - Refactoring

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

Refactorizacin - Refactoring

Customer.statement()
public String statement() { 1 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_RELEASE: thisAmount += each.getDaysRented() * 3; break; case Movie.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; }

Refactorizacin - Refactoring

Customer.statement()
2
// add frequent renter points frequentRenterPoints ++; // add bonus for a 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) + " frequent renter points"; return result; }

Refactorizacin - Refactoring

Customer.statement()
aCustomer aRental aMovie

statement * [for all rentals] getMovie getPriceCode getDaysRented

Rental Record for Dinsdale Pirhana Monty Python and the Holy Grail 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

3.5

Refactorizacin - Refactoring

Comentarios
El programa funciona perfectamente Se observan problemas de diseo
Mtodo excesivamente largo en la clase Client.statement() Dificultan futuros cambios

Cambios en los requerimientos


Producir una versin de HTML para statement
Solucin 1: copy & paste statement -> problemas de cdigo duplicado

La clasificacin de pelculas pronto cambiar, se desconoce la clasificacin definitiva


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

Cuando haya que aadir una nueva caracterstica al programa y el cdigo no mantiene la estructura adecuada para aadir la nueva caracterstica, primero se refactoriza el programa y luego se aade la nueva caracterstica.
10

Refactorizacin - Refactoring

Testear
Construir un conjunto slido de test
Crear unos clientes Dar a los clientes unos alquileres de varias clases de pelculas Generar manualmente cadena resultantes del alquiler Comparar las cadenas resultantes con las generadas manualmente

Localiza los posibles fallos (bugs) cuando se refactoriza Usar un simple framework para construir los Test
http://www.junit.org

Comprobar la ejecucin correcta de todos los test


Ejecutar el suite de todos los test como parte del proceso de construccin.

11

Refactorizacin - Refactoring

Extract Method
Se dispone de un fragmento de cdigo que puede ser agrupado junto. Crear un nuevo mtodo con ese cdigo, cuyo nombre explique el propsito del mtodo.
void printOwing() { printBanner(); //print details System.out.println ("name: System.out.println ("amount }

" + _name); " + getOutstanding());

void printOwing() { printBanner(); printDetails(getOutstanding()); } void printDetails (double outstanding) { System.out.println ("name: " + _name); System.out.println ("amount " + outstanding); }

12

Refactorizacin - Refactoring

Extract Method
Crear un mtodo con el nombre que describa la intencin del cdigo Copiar en el nuevo mtodo el cdigo extrado Determinar si las variables utilizadas en el cdigo extrado son:
Parmetros Valores de retorno Variables locales

Compilar Sustituir el fragmento de cdigo con llamadas al nuevo mtodo Compilar y testear

13

Refactorizacin - Refactoring

Cdigo 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 line 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; }

14

Refactorizacin - Refactoring

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

15

Refactorizacin - Refactoring

Statement() despus de la extraccin


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

16

Refactorizacin - Refactoring

Fallo de Testeo
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

thisAmount=amountFor(each)

} private int amountFor(Rental each) {

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

...
return this amount; }

La refactorizacin cambia los programas en pequeos pasos. Si se comete un error es ms fcil encontrar el fallo.

17

Refactorizacin - Refactoring

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 cdigo debera comunicar claramente que hace, el nombre de las variables es clave para conseguirlo.
18

Refactorizacin - Refactoring

Move Method
Un mtodo esta usando ms caractersticas de otra clase que de la clase en la que est definido. Crear un nuevo mtodo con un cuerpo similar en la clase que ms lo usa. Transformar el nuevo mtodo en una delegacin o eliminarlo completamente.

Class 1 aMethod()

Class 1

Class 2

Class 2 aMethod()

19

Refactorizacin - Refactoring

Move Method
Declarar el mtodo en la clase destino Copiar y ajustar el cdigo Colocar una referencia del objeto destino en el objeto fuente Transformar el mtodo original en un mtodo delegado
amountOf(Rental each) {return each.charge()}

Compilar y testear Encontrar todos los usuarios de ese mtodo


Transformarles para llamar al mtodo sobre la clase destino

Eliminar el mtodo original Compilar y testear

20

Refactorizacin - Refactoring

Aplicacin Move Method


Rental.getCharge()
//class Rental

double 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; Rental
Movie

priceCode: int

daysRented: int getCharge()

Customer statement()

21

Refactorizacin - Refactoring

Aplicacin Move Method


Customer.amountFor()
//class Customer 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

thisAmount=each.getCharge();

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

Si el mtodo movido es privado no tiene sentido mantener el delegado por que no van a existir clientes del mismo.
22

Refactorizacin - Refactoring

Problemas con variables temporales


//class Customer 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 = each.getCharge();
// add frequent renter point frequentRenterPoints ++; // add bonus for a 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) + " frequent renter points; return result; }

23

Refactorizacin - Refactoring

Problemas con variables temporales


mbito temporal y local
Aumentan complejidad - > aumentan tamao de los mtodos Pueden provocar duplicacin de cdigo

Dificultan la trazabilidad del cdigo Su eliminacin tiene un coste de rendimiento


Gran parte del tiempo de ejecucin esta localizado en pequeas porciones de cdigo Fcil localizar esos fragmentos de cdigo La mejor forma para optimizar es primero escribir un programa bien factorizado y luego optimizar

24

Refactorizacin - Refactoring

Replace Temp with Query


Se esta utilizando una variable temporal para manejar el resultado de una expresin. Extraer la expresin en un mtodo. Sustituir todas las referencias de la variable por la invocacin al mtodo. El nuevo mtodo puede ser luego utilizado en otros mtodos.
double basePrice = _quantity * _itemPrice; if (basePrice > 1000) return basePrice * 0.95; else return basePrice * 0.98;

if (basePrice() > 1000) return basePrice() * 0.95; else return basePrice() * 0.98; ... double basePrice() { return _quantity * _itemPrice; }

25

Refactorizacin - Refactoring

Replace temp with Query


Encontrar las variables temporales en las sentencias de asignacin que son asignadas una nica vez.
Java -> final y compilar

Extraer el lado derecho de la asignacin en un nuevo mtodo


A priori mtodo como privado Asegurarse que no se modifica ningn objeto

Reemplazar todas las referencias de la variable temporal por el nuevo mtodo Eliminar la declaracin y la sentencia de asignacin de la variable temporal Compilar y testear
26

Refactorizacin - Refactoring

Aplicacin Replace Temp with Query


Customer.statement()
//class Customer public 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 point frequentRenterPoints ++; // add bonus for a 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(each.getCharge();) + "\n; totalAmount += each.getCharge();; }


//add footer lines result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points; return result; }

27

Refactorizacin - Refactoring

Aplicacin Extract y Move Method


Rental.getFrequentRenterPoints()
//class Customer public 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 rental result += "\t" + each.getMovie().getTitle()+ "\t" + String.valueOf(each.getCharge()) + "\n"; totalAmount += each.getCharge(); } //add footer lines
result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " +String.valueOf(frequentRenterPoints) + " frequent renter points";

return result; }

28

Refactorizacin - Refactoring

UML: Despus de refactorizar


Rental Movie priceCode: int

daysRented: int getCharge() getFrequentRenterPoints()

Customer statement()

aCustomer

aRental

aMovie

statement * [for all rentals] getCharge getPriceCode getFrequentRenterPoints getPriceCode

29

Refactorizacin - Refactoring

Eliminacin variables temporales


totalAmount - frequentRenterPoints
//class Customer public 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 rental result += "\t" + each.getMovie().getTitle()+ "\t" + String.valueOf(each.getCharge()) + "\n"; totalAmount += each.getCharge(); } //add footer lines
result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " +String.valueOf(frequentRenterPoints) + "frequent renter points";

return result; } 30

Refactorizacin - Refactoring

Aplicacin Replace Temp with Query


Nuevos mtodos: 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; }
31

Refactorizacin - Refactoring

Aplicacin Replace Temp with Query


Customer.statement( )
//Customer public String statement() {

//Remove declarations
Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); //show figures for this rental result += "\t" + each.getMovie().getTitle()+ "\t" + String.valueOf(each.getCharge()) + "\n"; } //add footer lines
result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n"; result += "You earned " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points";

return result; }
32

Refactorizacin - Refactoring

UML: Despus de refactorizar


Rental Movie priceCode: int Customer

daysRented: int getCharge() getFrequentRenterPoints()

statement() getTotalCharge() getTotalFrequentRenterPoints()

aCustomer

aRental

aMovie

statement getTotalCharge * [for all rentals] getCharge

getPriceCode

getTotalFrequentRenterPoints * [for all rentals] getFrequentRenterPoints getPriceCode

33

Refactorizacin - Refactoring

Aadir funcionalidad
Customer.htmlStatement()
//Customer public 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 rental result += each.getMovie().getTitle()+ ": " + String.valueOf(each.getCharge()) + "<BR>\n"; } //add footer lines result += "<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; }
34

Refactorizacin - Refactoring

Clasificacin desconocida de pelculas


//class Rental double 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; }
35

Refactorizacin - Refactoring

Aplicacin Move Method


Movie.getCharge()
//class Rental double getCharge(){ return _movie.getCharge(_daysRented); } //class Movie 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;
}

Hacer lo mismo con Rental.frequentRenterPoints()

36

Refactorizacin - Refactoring

Clasificacin por herencia


Permite reemplazar las sentencias switch por polimorfismo Problema
Un pelcula puede cambiar su clasificacin durante su vida Un objeto no puede cambiar de clase durante su vida

Solucin aplicar el patrn de diseo Estado (State)


Movie getCharge

Movie getCharge

Price

getCharge

return price.getCharge

Regular Movie getCharge

Childrens Movie getCharge

New Release Movie getCharge

Regular Price getCharge

Childrens Price getCharge

New Release Price getCharge

37

Refactorizacin - Refactoring

Replace Type Code with State/Strategy


Se dispone para clasificar de un cdigo-tipo que afecta al comportamiento de una clase, pero no puede usarse clasificacin por herencia. Replace the type code with a state object.

Employee ENGINEER : int SALESMAN : int ty pe : int

Employee

Employee Type

Engineer

Salesman

38

Refactorizacin - Refactoring

Replace Type Code with State/Strategy


Crear una nueva clase para el cdigo - tipo Aadir subclases del objeto estado, una por cada cdigo - tipo Crear un mtodo abstracto en la superclase que retorne el cdigo-tipo. Definir en las subclases dicho mtodo, retornando el codigo tipo correcto. Compilar Crear un campo en la clase original para referenciar al objeto estado. Cambiar los mtodos de la clase original para la obtencin del cdigo tipo por una delegacin sobre el objeto estado. Cambiar el codigo tipo poniendo mtodos para asignar una instancia de una subclase Compilar y testear.
39

Refactorizacin - Refactoring

Transformar cdigo precio en jerarqua 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; } }

40

Refactorizacin - Refactoring

Cambiar metodos de acceso en Movie


//Class Movie public 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; 41

//Class Movie public int getPriceCode() { return _priceCode; } public setPriceCode (int arg){ _priceCode = arg; } private int _priceCode;

Refactorizacin - Refactoring

Replace Conditional With Polymorphism


Se tiene una sentencia condicional que selecciona diferentes comportamientos dependiendo del tipo de objeto. Mover cada rama condicional a un mtodo redefinido en una subclase. Transformar el mtodo 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"); }

Bird getSpeed

European getSpeed

African getSpeed

Norweigian Blue getSpeed

42

Refactorizacin - Refactoring

Replace Conditional with Polymorphism


Mover la sentencia condicional a la superclase de la jerarqua de herencia Copiar el cuerpo de cada sentencia condicional como el cuerpo del mtodo redefinido en una subclase Compilar y testear Repetir el mismo proceso para cada rama condicional Sustituir las sentencias condicionales por un mtodo abstracto

43

Refactorizacin - Refactoring

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

44

Refactorizacin - Refactoring

Replace Conditional with Polymorphism


Price.getCharge( )
//Class RegularPrice double 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 Price abstract double getCharge(int daysRented);

Hacer lo mismo con Rental.getFrequentRenterPoints()

45

Refactorizacin - Refactoring

Resumen
En el ejemplo se mejoro el diseo de un programa para aadir funcionalidad
Ms fcil aadir nuevos servicios a los clientes Ms fcil aadir nuevos tipos de pelculas

Dismuye depuracin/debugging durante el proceso de refactorizacin


Las transformaciones controladas reducen la probabilidad de fallos Las pequeas transformaciones hacen que los fallos sean ms fciles 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 catlogo de Fowler en: http://www.refactoring.com/


46

Refactorizacin - Refactoring

Tabla de contenidos
Introduccin: un ejemplo de refactoring

Principios en refactoring
Definiciones y orgenes Por qu y cuando refactorizar? Problemas con las refactorizaciones
Miscelneo:
Herramientas Metodologas giles Definicin de nuevas refactorizaciones (Big Low) Conclusiones Fowler, Refactoring: Improving the Design of Existing Code, AddisonWesley, 1999
47

Bibliografa

Refactorizacin - Refactoring

Definiciones y orgenes de Refactoring


Uso relajado
Reorganizar un programa o algo

Como nombre
Cambio en la estructura interna de un programa para facilitar su comprensin y abaratar sus modificaciones, sin modificar el comportamiento externo

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

Origenes
Ward Cunningham y Kent Beck (Smalltalk) Ralph Johnson y Bill Opdyke (Definicin formal d) ftp://st.cs.uiuc.edu/pub/papers/refactoring/opdyke-thesis.ps.Z John Brant y Don Roberts (Refactoring Browser)

48

Refactorizacin - Refactoring

Cuando refactorizar?
Aadir nueva funcionalidad
Refactorizar el cdigo existente hasta que se entienda Refactorizar el diseo para facilitar la incorporacin de la nueva funcionalidad

Para encontrar fallos


Refactorizar para entender el cdigo

Para revisiones de cdigo


Efecto inmediato de una revisin de cdigo

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.
49

Refactorizacin - Refactoring

Metfora de los dos sombreros

Aadir funcionalidad No modifica el cdigo existente Aade nueva funcionalidad sistema Aade nuevos test Obtener los resultados de test

Refactorizar

al

Modifica el cdigo existente No aade nuevas caractersticas No aade test (pero podra modificar alguno)
Cambio de interfaz

Reestructura el cdigo existente para eliminar redundancia

Realizar intercambios de sombreros, pero llevar puesto slo uno cada vez
50

Refactorizacin - Refactoring

Por qu refactorizar?
Para mejorar el diseo del software
Combate la perdida de la estructura de cdigo (diseo) en cambios acumulativos Facilita futuros cambios en el programa

Hacer el software ms fcil de entender


Escribir para que lo entienda las personas, no el compilador Entender cdigo desarrollado por otros

Ayuda a encontrar errores


Refactorizar mientras se depura para clarificar el cdigo www.junit.org www.xprogramming.com/software
Refactorizar ayuda a programar ms rpido
51

Refactorizacin - Refactoring

Problemas con la refactorizacin


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
Aadir flexibilidad -> asla los cambios de los distintos mdulos a costa de aadir complejidad a la estructura

Interfaces publicadas -> ms all del modificador public


Si una refactorizacin cambia la signatura de un mtodo, no existe problema si se tiene acceso a todas las llamadas a ese mtodo Publicar slo cuando sea necesario
Un cambio obliga a mantener la antigua interface -> problema de cdigos legado

Publicar dentro del equipo de desarrollo

Trabajar sin casos de prueba definidos


Para refactorizar el cdigo debe funcionar correctamente sino reescribirlo
52

Refactorizacin - Refactoring

Tabla de contenidos
Introduccin: un ejemplo de refactoring Principios en refactoring
Definiciones y orgenes Por qu y cuando refactorizar? Problemas con las refactorizaciones

Miscelneo:
Herramientas Metodologas giles
Definicin de nuevas refactorizaciones (Big Low)

Conclusiones
Bibliografa
Fowler, Refactoring: Improving the Design of Existing Code, AddisonWesley, 1999
53

Refactorizacin - Refactoring

Herramientas de Refactoring
Basadas en transformaciones demostrables
Repositorio para consultar informacin
Entidades del programa: clases, mtodos, instrucciones, Encontrar todos los mtodos que leen o escriben una var. de instancia

Construir parses de programas Probar matemticamente que la refactorizacin no cambia la semntica Incorporar las refactorizaciones en entornos integrados de desarrollo

Aumentan la velocidad en el proceso de refactorizacin


Operaciones para realizar Extract method con una herramienta
Seleccionar cdigo a extraer Escribir el cdigo del mtodo

No son ciencia ficcin - > falta madurar


54

Refactorizacin - Refactoring

Herramientas de Refactoring

55

Refactorizacin - Refactoring

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 cmo escribir el cdigo


Para preservar el comportamiento en los cambios

Los test deben comprobarse automticamente (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 comando Testear con cada compilacin Direcciones de inters

56

Refactorizacin - Refactoring

Decisiones de diseo
Diseo planeado
Considera necesidades actuales y posibles necesidades futuras Se disea para minimizar los cambios con futuras necesidades Aade un parche en el cdigo si aparece alguna necesidad imprevista

Diseo evolutivo
Considerar necesidades actuales y posibles necesidades futuras Evaluar el coste de la actual flexibilidad versus coste de refactorizar ms tarde Refactorizar cuando el cambio aparece

XP
Metodologa desarrollada por Kent Beck Diseada para soportar los cambios Prcticas claves
Desarrollo iterativo Cdigo Self Testing Refactoring Programacin por parejas

Influencia de las refactorizaciones para dar soporte el diseo evolutivo

57

Refactorizacin - Refactoring

Tcnicas de equipo
Animar a la cultura de la refactorizacin
Nadie hace bien algo la primera vez Nadie puede escribir cdigo 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

Programacin por parejas


Dos programadores trabajando juntos pueden ser ms rpidos que trabajando separadamente Refactorizar con escritor de la clase y con un usuario de la misma

58

Refactorizacin - Refactoring

Crear refactorizaciones propias


Considerar un cambio en el programa Debera cambiar el comportamiento externo del programa Desglosar el cambio en pequeos 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 algn problema, considerar cmo eliminarlo en el futuro

Realizar los cambios de nuevo, comprobar y redefinir las anotaciones Despus de dos o tres veces de ejecutar la refactorizacin se dispondr de una nueva refactorizacin
59

Refactorizacin - Refactoring

Conclusiones
El principal beneficio de los objetos es su facilidad para ser cambiados. (Encapsulamiento) Las refactorizaciones permiten mejorar el diseo una vez que este ha sido escrito. El tema de Refactoring esta inmaduro:
Falta de documentacin escrita Falta y mejora de herramientas

60

También podría gustarte