¿Por qué?

Mito
[irónicamente] un proyecto de software bien administrado lleva a cabo un desarrollo metódico de requerimientos y define una lista estable de las responsabilidades del programa. El diseño sigue los requerimientos, y se hace con cuidado para que la codificación pueda proceder de forma lineal, de principio a fin, lo que implica que la mayor parte del código se puede escribir, probar y olvidar de una sola vez …​
— Fred P. Brooks Jr
The Mythical Man-Month
Realidad
  • Triangulo de Hierro, coste, tiempo y ámbito dependen de la calidad

Creo firmemente que un buen diseño es esencial para un rápido desarrollo de software. Sin un buen diseño, puede progresar rápidamente durante un tiempo, pero pronto el diseño deficiente comienza a ralentizarlo. Dedicas tiempo a buscar y corregir errores en lugar de agregar una nueva funcionalidad. Los cambios requieren más tiempo para comprender el sistema y encontrar el código duplicado. Las nuevas funcionalidades necesitan más codificación a medida que parchea un parche que parchea un parche sobre la base del código original.
— Martin Fowler
Refactoring
  • Ley de Complejidad Creciente de Lemmany & Belady

No hay software tan grande, enredado o complejo que el mantenimiento no pueda empeorarlo
— Gerald Weinberg
A medida que las personas cambian el código, cambios para lograr objetivos a corto plazo o cambios realizados sin una comprensión completa del diseño del código, el código pierde su estructura. Se vuelve más difícil ver el diseño leyendo el código. La pérdida de la estructura del código tiene un efecto acumulativo. Cuanto más difícil es ver el diseño en el código, más difícil es conservarlo y más rápidamente se deteriora. El problema es que cuando intenta que el programa funcione, no está pensando en el futuro desarrollador.
— Martin Fowler
Refactoring

¿Qué?

Un cambio hecho a la estructura interna del software para hacerlo más fácil de entender y más barato de modificar, sin modificar su comportamiento observable
— Martin Fowler
Refactoring
Sustantivo Verbo
Cada una de las modificaciones hechas al código fuente que cumplen con la definición
— Martin Fowler
Refactoring
El acto de aplicar uno o varios refactorings al código fuente
— Martin Fowler
Refactoring
I should amplify a couple of points in my definitions:
Primero, el propósito de la refactorización es hacer que el software sea más fácil de entender y modificar. Puedes realizar muchos cambios en el software que hacen poco o ningún cambio en el comportamiento observable. Solo los cambios realizados para facilitar la comprensión del software son refactorizaciones. Un buen contraste es la optimización del rendimiento.
— Martin Fowler
Refactoring
¿La refactorización es solo limpiar el código? "En cierto modo, la respuesta es sí, pero creo que la refactorización va más allá porque proporciona una técnica para limpiar el código de una manera más eficiente y controlada.
— Martin Fowler
Refactoring
Sí cumple No cumple
  • Mejorar la calidad diseño del código después de haberlo escrito

  • Mantener el comportamiento

  • con una técnica precisa: pequeños pasos, baby steps

  • Optimización de recursos, tiempo, espacio, rendimiento, …​, mejorar la eficiencia del código, …​

  • Incorporar nueva funcionalidad

  • Rehacer/Limpiar código sin baby steps

¿Para qué?

Ventaja Justificación
  • Comprender el software fácilmente

La refactorización le ayuda a hacer su código más legible. Al refactorizar, tiene un código que funciona pero no está estructurado idealmente. Un poco de tiempo dedicado a la refactorización puede hacer que el código comunique mejor su propósito. No soy necesariamente altruista al respecto. A menudo, este futuro desarrollador soy yo. A medida que el código se aclara, encuentro que puedo ver cosas sobre el diseño que no podía ver antes.
— Martin Fowler
Refactoring
  • Localizar errores por revisiones, más seguro (eficaz)

Sin embargo, encuentro que si refactorizo el código, trabajo profundamente en la comprensión de lo que hace el código, y vuelvo a poner esa nueva comprensión en el código. Al aclarar la estructura del programa, aclaro ciertas suposiciones que hice, hasta el punto en que ni siquiera yo puedo evitar detectar los errores
— Martin Fowler
Refactoring
  • Programar más rápido (eficiente)

Se necesita un cambio de ritmo para realizar cambios que faciliten la comprensión del código. La refactorización le ayuda a desarrollar software más rápidamente, porque evita que el diseño del sistema se deteriore
— Martin Fowler
Refactoring
  • Mejorar la calidad de un diseño (efectividad)

Sin la refactorización, el diseño del programa se deteriorará. La refactorización metódica ayuda a que el código conserve su forma.
— Martin Fowler
Refactoring
Excusas Error
  • Desconocimiento

  • Inefectividad al no invertir en aprender nuevas técnicas, herramientas, diseños, …​

  • Si no está roto, no lo toques!

  • Asunción de riesgos por miedo, pereza, vergüenza, …​

  • Propiedad privada del código

  • Ausencia de control de versiones (CVS) del código del proyecto que no tiene dueño, es del equipo porque, en caso contrario, habría un cuello de botella de máximo riesgo con ese “dueño”

  • No se puede saber quién y cuántos son los afectados

  • Ausencia de control de versiones de interfaz (CMD)

  • La gestión del proyecto no lo permite

  • La gestión no requiere que justifiquemos cada “for” que escribimos y la Refactorización debe ser poco a poco, baby steps, por ejemplo: extraer método, extraer clase, subir un campo, método genérico, sustituir algoritmo, …​

  • Arquitectura centrada en Bases de Datos

  • Lógica de negocio acoplada a la estructura de la BBDD con migración ardua y compleja sin herramientas

¿Cómo?

Bucle de Refactoring

assert <invariante>;
while (<condición>) {
  <cuerpo>;
  assert <invariante>;
}
assert <invariante>;
assert <mismo comportamiento>;
while (<smell code>) {
  <refactorización>;
  assert <mismo comportamiento>;
}
assert <mismo comportamiento>;

Invariante del Refactoring

Red de Seguridad con Pruebas Unitarias Automáticas suficientes para asegurar el mismo comportamiento

No combinar con modificar/agregar comportamiento al mismo tiempo que refactorizas porque pierdes la Red de Seguridad

  • Fáciles de Ejecutar

    • Automatizadas

    • Auto-verificables

    • Repetibles

    • Independientes

    • Rápidas

  • Fáciles de Leer/Escribir

    • Cohesivas

    • Sencillas

    • Expresivas

  • Mejoran la Calidad

    • Repelentes de Errores

    • Localizan Defectos

    • Parte de la Especificación

  • Fáciles de Mantener

    • Profesionales

  • Mejoran la Comprensión del SUT

    • Parte de la Documentación

  • Reducen los Riesgos

    • Inocuas

    • Red de Seguridad

Condición del Refactoring

La perfección se alcanza, no cuando no hay nada que añadir, sino cuando no queda nada que quitar
— Antoine de Saint Exupéry
El principito
Reglas del diseño simple: pasa todas las pruebas, no contiene duplicados, expresa claramente la intención del programador y minimiza el número de clases y métodos para expresar dicha intención
— Kent Beck
XXX
Smell Codes de Martin Fowler Smell Codes de Robert Martin
  • Duplicated Code

  • Duplication

  • Long Method

  • Be Precise

  • Large Class

  • Functions Should Descend Only One Level of Abstraction

  • Long Parameter List

  • Too Many Arguments

  • Divergent Change

  • Avoid Negative Conditionals

  • Shotgun Surgery

  • Inapropiate Static

  • Feature Envy

  • Feature Envy

  • Data Clumps

  • Don’t Be Arbitrary

  • Primitive Obsession

  • Make Logical Dependencies Physical

  • Switch Statements

  • Hidden Temporal Couplings

  • Parallel Inheritance Hierarchies

  • Avoid Encodings

  • Lazy Class

  • Obscured Intent

  • Speculative Generality

  • Undestand the Algorithm

  • Temporary Field

  • Functions Should Do One Thing

  • Message Chains

  • Unambiguous Names

  • Middle Man

  • Dead Function

  • Inappropriate Intimacy

  • Base Class Depending on Their Derivatives

  • Alternative Classes with Different Interfaces

  • Don’t Inherit Constants

  • Incomplete Library Class

  • Constants vs Enums

  • Data Class

  • Obvious Behaviour is Unimplemented

  • Refused Bequest

  • Too Much Information

  • Comments

  • Commented-Out Code

  • …​

  • Principios, Leyes, heurísticas, …​

  • Conceptos Recurrentes

  • KISS, YAGNI

  • Formato, Comentarios, Nombrado

  • Estándares, Consistencia

  • Alertas, Código Muerto, DRY

  • Alta Cohesión

  • Bajo Acoplamiento

  • Pequeña Granularidad

  • Modelo, Vista, Controlador

  • Interfaz Suficiente, Completa y Primitiva

  • Principio de Menor Sorpresa

  • Diseño por Contrato

  • Principio de Sustitución de Liskov

  • Principio de Separación de Interfaces

  • Principio Abierto/Cerrado de Meyer

  • Herencia vs Delegación

  • Inversión de Control e Inyección de Dependencias

  • Técnica de Doble Despacho

  • Modelos

  • Abstracción

  • Ordenación

  • Asociación

  • Seguridad

  • Reusabilidad

  • Eficacia

  • Eficiencia

  • Compromisos

  • Complejidad

  • Evolución

  • Patrones Creacionales, Estructurales y de Comportamiento

Cuerpo del Refactoring

  • Un Smell Code se elimina con uno de entre varios posibles Refactoring, dependiendo del contexto del Smell Code

  • Un Refactoring puede componerse de otros Refactoring

    • Un Refactoring se compone de varios Baby Steps

cuerpo
Refactorizaciones correspondientes a smell codes
  • Código Duplicado: se tienen las siguientes posibles refactorizaciones:

    • Extraer método: la misma expresión en dos método de la misma clase

    • Extraer método y subir un campo: la misma expresión en dos clases hermanas

    • Extraer método y método genérico: código similar pero no igual

    • Sustituir algoritmo y extrare método: los métodos hacen la misma cosa con algoritmos diferentes

    • Extraer clase y extraer métodos: en dos clases no relacionadas.

duplicado
  • Clase Grande: se tienen las siguientes posibles refactorizaciones:

    • Extraer clase: con subconjunto de atributos relacionados

    • Extraer subclase: si tiene sentido

grande
Baby Steps de Extraer Clase
  • Extraer Clase se tienen los siguientes baby steps

    • Decide cómo partir las responsabilidades de la clase

    • Crea una nueva clase para expresar dicho reparto

      • Si las responsabilidades de la clase original no corresponde con su nombre, cambialo

    • Haz un enlace de la clase original a la nueva

      • Puedes necesitar un doble enlace pero no lo hagas hasta que no encuentres necesario

    • Aplicar Mover Campo para cada campo que quieras mover

    • Compila y pasa todos los test después de cada movimiento

    • Aplicar Mover Método para cada método de la clases original a la nueva empezando por los de más bajo nivel (llamados, no llamantes) y construye hasta el nivel más alto

    • Compila y pasa todos los test después de cada movimiento

    • Revisa y reduce los interfaces de las clases

      • Decidir si exponer la nueva clase

    • En caso afirmativo, refactorizar interfaces

extraersubClase
  • Extraer subclase se tienen los siguientes baby steps

    • Define a new subclass of the source class

    • Provide constructors for the new subclass

      • In the simple cases, copy the arguments of the superclass and call the superclass constructor with super

      • If you want to hide the use of the subclass from clients, you can use Replace Constructor with Factory Method

    • Find all calls to constructors of the superclass. If they need the subclass, replace with a call to the new constructor

      • If the subclass constructor needs different arguments, use Rename Method to change it. If some of the constructor parameters of the superclass are no longer needed, use Rename Method on that too.

      • If the superclass can no longer be directly instantiated, declare it abstract

    • One by one use Push Down Method and Push Down Field to move features onto the subclass

      • Unlike Extract Class it usually is easier to work with the methods first and the data last

      • When a public method is pushed, you may need to redefine a caller’s variable or parameter type to call the new method. The compiler will catch these cases

    • Look for any field that designates information now indicated by the hierarchy (usually a boolean or type code). Eliminate it by using Self Encapsulate Field and replacing the getter with polymorphic constant methods. All users of this field should be refactored with Replace Conditional with Polymorphism

      • For any methods outside the class that use an accessor, consider using Move Method to move the method into this class; then use Replace Conditional with Polymorphism

extraerClase

Catálogo de Refactorizaciones

No se convierte en un artesano del software aprendiendo una lista de heurísticas. El profesionalismo y la artesanía provienen de valores que impulsan las disciplinas
— Martin Fowler
Refactoring
  • Nombrado

    • Replace Magic Number with Symbolic Constant

    • Rename Method

  • Parámetros

    • Remove Assignments to Parameters

    • Replace Parameter with Method

    • Replace Parameter with Explicit Methods

  • Variables Temporales

    • Split Temporary Variable

    • Introduce Explaining Variable

    • Inline Temp

    • Replace Temp with Query

  • Condicionales

    • Replace Nested Conditional with Guard Clauses

    • Consolidate Conditional Expression

    • Consolidate Duplicate Conditional Fragments

    • Remove Control Flag

    • Introduce Null Object

    • Decompose Conditional

  • Cabecera de Métodos

    • Remove Parameter

    • Add Parameter

    • Parameterize Method

  • Cuerpo de Métodos

    • Substitute Algorithm

    • Inline Method

    • Extract Method

    • Replace Method with Method Object

    • Form Template Method

  • Encapsulación

    • Encapsulate Field

    • Self Encapsulate Field

    • Hide Method

    • Remove Setting Method

    • Encapsulate Collection

    • Encapsulate Downcast

  • Gestión de Errores

    • Introduce Assertion

    • Replace Error Code with Exception

    • Separate Query from Modifier

    • Replace Exception with Test

  • Relación de Herencia

    • Extract Interface

    • Extract Superclass

    • Extract Subclass

    • Collapse Hierarchy

    • Replace Subclass with Fields

    • Pull Up Field

    • Push Down Field

    • Pull Up Method

    • Push Down Method

    • Pull Up Constructor Body

  • Polimorfismo

    • Replace Conditional with Polymorphism

    • Replace Type Code with Subclasses

    • Replace Type Code with State/Strategy

    • Replace Constructor with Factory Method

  • Reparto de Responsabilidades

    • Move Method

    • Move Field

    • Inline Class

    • Extract Class

    • Replace Type Code with Class

    • Replace Array with Object

    • Replace Record with Data Class

    • Introduce Parameter Object

    • Replace Data Value with Object

    • Change Value to Reference

    • Change Reference to Value

  • Colaboraciones

    • Preserve Whole Object

    • Replace Delegation with Inheritance

    • Replace Inheritance with Delegation

    • Hide Delegate

    • Remove Middle Man

    • Change Unidirectional Association to Bidirectional

    • Change Bidirectional Association to Unidirectional

  • Bibliotecas & GUI

    • Introduce Foreign Method

    • Introduce Local Extension

    • Duplicate Observed Data

Nombrado

Rename Method
renameMethod1
  • Motivation: The name of a method does not reveal its purpose

  • Mechanics: Change the name of the method.

renameMethod2
Replace Magic Number with Symbolic Constant
double potentialEnergy(double mass,double height)  {
   return mass * 9.81 * height;
}
  • Motivation: You have a literal number with a particular meaning

  • Mechanics: Create a constant, name it after the meaning, and replace the number with it.

double potentialEnergy(double mass,double height) {
    return mass * GRAVITATIONAL_CONSTANT * height;
}
static final double GRAVITATIONAL_CONSTANT = 9.81;

Parámetros

Remove Assignments to Parameters
int discount (int inputVal, int quantity, int yearToDate)  {
  if (inputVal > 50)
    inputVal -=2;
  ...
  • Motivation: The code assigns to a parameter

  • Mechanics: Use a temporary variable instead.

int discount (int inputVal, int quantity, int yearToDate)  {
  int result = inputVal ;
  if (inputVal > 50)
    result -=2 ;
  ...
Replace Parameter with Method
int basePrice = quantity * itemPrice;
discountLevel = getDiscountLevel();
double finalPrice = discountedPrice (basePrice, discountLevel);
  • Motivation: An object invokes a method, then passes the result as a parameter for a method. The receiver can also invoke this method

  • Mechanics: Remove the parameter and let the receiver invoke the method

int basePrice = quantity * itemPrice;
double finalPrice = discountedPrice (basePrice);
Replace Parameter with Explicit Methods
void setValue (String name, int vale) {
  if (name.equals("height") )
     height = value;
  if (name.equals ("width") )
     width = value;
  Assert.shouldNeverReachHere() ;
}
  • Motivation: You have a method that runs different code depending on the values of an enumerated parameter

  • Mechanics: Create a separate method for each value of the parameter

void setHeight (int arg) {
   height = arg ;
}
void setWidht (int arg) {
   width = arg ;
}

Variables Temporales

Split Temporary Variable
double temp = 2*(height+width);(1)
System.out.println (temp);
temp = height*width;(1)
System.out.println (temp);
  • Motivation: You have a temporary variable assigned to more than once, but is not a loop variable nor a collecting temporary variable

  • Mechanics: Make a separate temporary variable for each assignment

final double perimeter = 2*(height+width);(1)
System.out.println (perimeter);
final double area = height*width;(1)
System.out.println (area);
Introduce Explaining Variable
if ((platform.toUpperCase().indexOf ("MAC") > -1) &&
    (browser.toUpperCase().indexOf("IE") > -1 )  &&
    wasInitialized() && resize > 0 ) {
     // do something
}
  • Motivation: You have a complicated expression

  • Mechanics: Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose.

final boolean isMacOs  = plataform.toUpperCase().indexOf("MAC") > -1;
final boolean is IEBrowser  = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResired  = resize > 0
if (isMacOs && isIEBrowser && wasInitialized()  && wasResired) {
     // do something
}
Inline Temp
double basePrice = anOrder.basePrice();
return (basePrice > 1000);
  • Motivation: You have a temp that is assigned to once with a simple expression, and the temp is getting in the way of other refactorings

  • Mechanics: Replace all references to that temp with the expression

return (anOrder.basePrice() > 1000);
Replace Temp with Query
double basePrice = quantity * itemPrice;
if (basePrice > 1000)
    return basePrice * 0.95;
else
    return basePrice * 0.98;
  • Motivation: You are using a temporary variable to hold the result of an expression

  • Mechanics: Extract the expression into a method. Replace all references to the temp with the expression. The new method can then be used in other methods

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

Condicionales

Replace Nested Conditional with Guard Clauses
double getPayAmount() {
  double result;
  if (_isDead)
    result = deadAmount();
  else {
    if (_isSeparated)
      result = separatedAmount();
    else {
      if (_isRetired)
        result = returedAmount();
      else
        result = normalPayAmount();
    }
  }
  return result;
}
  • Motivation: A method has conditional behavior that does not make clear the normal path of execution

  • Mechanics: Use guard clauses for all the special cases

double getPayAmount() {
  if (_isDead)
    return deadAmount();
  if (_isSeparated)
    return separatedAmount();
  if (_isRetured)
    return retiredAmount();
  return normalPayAmount();
}
Consolidate Conditional Expression
double desabilityAmount() {
  if (_seniority <2)
    return 0 ;
  if (_monthsDisabled >12)
    return 0 ;
  if (_isPartTime)
    return 0 ;
  ...
  //compute the disability deadAmount
  • Motivation: You have a sequence of conditional tests with the same result

  • Mechanics: Combine them into a single conditional expression and extract it

double disabilityAmount() {
  if (isNotEligableForDisability() )
    return 0 ;
  ...
  //compute the disability deadAmount
Consolidate Duplicate Conditional Fragments
if (isSpecialDeal()) {
   total = price*0.95;
   send();
} else {
   total = price*0.98;
   send();
}
  • Motivation: The same fragment of code is in all branches of a conditional expression

  • Mechanics: Move it outside of the expression

if (isSpecialDeal() )
  total = price * 0.95 ;
else
  total = prise * 0.98 ;
send();
Remove Control Flag
set done to false
while not done
  if (condition)
     do something
     set done to true
  next step of loop
  • Motivation: You have a variable that is acting as a control flag for a series of boolean expressions

  • Mechanics: Use a break or return instead

while not done
  if (condition)
     do something
     break/return
  next step of loop
Introduce Null Object
if (customer == null)
  plan = BillingPlan.basic();
else
  plan = customer.getPlan();
  • Motivation: You have repeated checks for a null value

  • Mechanics: Replace the null value with a null object

NullObject
Decompose Conditional
if (date.before(SUMMER_START) || date.after (SUMMER_END))
   charge = quantity * winterRate+winterServiceCharge;
else
   charge = quantity * summerRate;
  • Motivation: You have a complicated conditional (if-then-else) statement

  • Mechanics: Extract methods from the condition, then part, and else parts.

if (notSummer(date) )
   charge = winterCharge(quantity);
else
   charge = summerCharge(quantity);

Cabecera de Método

Remove Parameter
remuveParameter1
  • Motivation: A parameter is no longer used by the method body.

  • Mechanics: Remove it.

remuveParameter2
Add Parameter
addParameter1
  • Motivation: A method needs more information from its calle

  • Mechanics: Add a parameter for an object that can pass on this information

addParameter2
Parameterize Method
parameterizeMethod1
  • Motivation: Several methods do similar things but with different values contained in the method body.

  • Mechanics: Create one method that uses a parameter for the different values

parameterizeMethod2

Cuerpo de Métodos

Substitute Algorithm
String foundPerson(String[] people ) {
  for (int i=0; i < people.lenght; i++) {
    if (people[i].equals ("Don"))  {
      return "Don" ;
    }
    if people[i].equals ("John")) {
      return "Jhon" ;
    }
    if (people[i].equals ("Kent"))  {
      return "Kent" ;
    }
  }
  return "" ;
}
  • Motivation: You want to replace an algorithm with one that is clearer

  • Mechanics: Replace the body of the method with the new algorithm

String foundPerson (String[] people) {
  List candidates = Arrays.asList (new String[] {"Don", "Jhon", "Kent"});
  for (int i=0; i< people.length; i++)
    if (candidates.contains(people[i]))
      return people[i];
  return "" ;
}
Inline Method
int getRating(){
  return (moreThanFiveLateDeliveries())? 2: 1;
}

boolean moreThanFiveLateDeliveries(){
  return numberOfLateDeliveries > 5;
}
  • Motivation: A method’s body is just as clear as its name

  • Mechanics: Put the method’s body into the body of its callers and remove the method

int getRating(){
  return (numberOfLateDeliveries > 5)? 2: 1;
}
Extract Method
void printOwing(double amount){
  printBanner() ;
  // print details
  System.out.println("name:" + name);
  System.out.println("amount:" + amount);
}
  • Motivation: You have a code fragment that can be grouped together

  • Mechanics: Turn the fragment into a method whose name explains the purpose of the method

void printOwing(double amount){
  printBanner();
  printDetails (amount);
}

void printDetails (double amount){
  System.out.println ("name:") + name);
  System.out.println ("amount:") + amount);
}
Replace Method with Method Object
class Order
  double price() {
    double prinaryBasePrice;
    double secondaryBasePrice;
    double tertiaryBasePrice;
    // long computation;
    ....
}
  • Motivation: You have a long method that uses local variables in such a way that you cannot apply Extract Method

  • Mechanics: Turn the method into its own object so that all the local variables become fields on that object. You can then decompose the method into other methods on the same object

methodObject
Form Template Method
templateMethod1
  • Motivation: You have two methods in subclasses that perform similar steps in the same order, yet the steps are different

  • Mechanics: Get the steps into methods with the same signature, so that the original methods become the same. Then you can pull them up

templateMethod2

Encapsulación

Encapsulate Field
public String  _name
  • Motivation: There is a public field

  • Mechanics: Make it private and provide accessors

private String name;

public String getName(){
  return this.name;
}

public void setName(String name){
  this.name = name;
}
Self Encapsulate Field
private int low;
private int high;

public boolean includes (int value) {
  return this.low <= value && value < = this.high;
}
  • Motivation: You are accessing a field directly, but the coupling to the field is becoming awkward

  • Mechanics: Create getting and setting methods for the field and use only those to access the field

private int low
private int high;

boolean includes (unt value) {
     return this.getLow() <= value && value <= this.getHigh() ;
}

int getLow() {
  return low;
}

int getHigh() {
  return high;
}
Hide Method
hideMethod1
  • Motivation: A method is not used by any other class

  • Mechanics: Make the method private

hideMethod2
Remove Setting Method
settingMethod1
  • Motivation: A field should be set at creation time and never altered.

  • Mechanics: Remove any setting method for that field

settingMethod2
Encapsulate Collection
encapsulateCollection1
  • Motivation: A method returns a collection

  • Mechanics: Make it return a read-only view and provide add/remove methods

encapsulateCollection2
Encapsulate Downcast
Object lastReading()  {
    return readings.lastElement() ;
}
  • Motivation: A method returns an object that needs to be downcasted by its callers

  • Mechanics: Move the downcast to within the method

Reading lastReading()  {
   return (Reading) readings.lastElement() ;
}

Gestión de Errores

Introduce Assertion
double getExpenseLimit() {
   // should have either exprense limit or a primary project
   return (_expenseLimit != NULL _EXPENSE) ?
      _expenseLimit:
      _primaryProject.getMemberExpenseLimit() ;
}
  • Motivation: A section of code assumes something about the state of the program

  • Mechanics: Make the assumption explicit with an assertion

double getExpenseLimit()  {
    Assert.isTrue (_expenseLimit !=NULL _EXPENSE | | _primaryProject != null);
    return (_expenseLimit ! = NULL _EXPENSE) ?
       _expenseLimit:
       _primaryProject.getMemberExpenseLimit() ;
}
Replace Error Code with Exception
int withdraw(int amount) {
    if (amount > _balance)
        return -1;
    else  {
        balance -= amount ;
        return 0 ;
    }
}
  • Motivation: A method returns a special code to indicate an error.

  • Mechanics: Throw an exception instead.

void withdraw(int amount) throws BalanceExeption {
    if (amount > _balance) throw new BalanceExeption() ;
    balance -=amount;
}
Separate Query from Modifier
Query1
  • Motivation: You have a method that returns a value but also changes the state of an object

  • Mechanics: Create two methods, one for the query and one for the modification

Query2
Replace Exception with Test
double getValueForPeriod (int periodNumber) {
   try  {
        return _values[periodNumber];
   }  catch (ArrayIndexOutOfBoundsException e) {
      return 0;
   }
}
  • Motivation: You are throwing a checked exception on a condition the caller could have checked first

  • Mechanics: Change the caller to make the test first

double getValueForPeriod (int periodNunber) {
   if (periodNumber >= _values.length) return 0;
   return _values(periodNumber);
}

Relación de Herencia

Extract Interface
extractInterface1
  • Motivation: Several clients use the same subset of a class’s interface, or two classes have part of their interfaces in common.

  • Mechanics: Extract the subset into an interface

extractInterface2
Extract Superclass
extractSuperclass1
  • Motivation: You have two classes with similar features

  • Mechanics: Create a superclass and move the common features to the superclass

extractSuperclass2
Extract Subclass
extractSubclass1
  • Motivation: A class has features that are used only in some instances

  • Mechanics: Create a subclass for that subset of features

extractSubclass2
Collapse Hierarchy
collapseHierarchy1
  • Motivation: A superclass and subclass are not very different

  • Mechanics: Merge them together

collapseHierarchy2
Replace Subclass with Fields
Fields1
  • Motivation: You have subclasses that vary only in methods that return constant data.

  • Mechanics: Change the methods to superclass fields and eliminate the subclasses

Fields2
Pull Up Field
upField1
  • Motivation: Two subclasses have the same field.

  • Mechanics: Move the field to the superclass

upField2
Push Down Field
pushDownField1
  • Motivation: A field is used only by some subclasses

  • Mechanics: Move the field to the superclass

pushDownField2
Pull Up Method
pullUpMethod1
  • Motivation: You have methods with identical results on subclasses

  • Mechanics: Move them to the superclass.

pullUpMethod2
Push Down Method
pushUpMethod1
  • Motivation: Behavior on a superclass is relevant only for some of its subclasses

  • Mechanics: Move it to those subclasses.

pushUpMethod2
Pull Up Constructor Body
class Manager extends Employee...
   public Manager (String name,String id, int grade) {
       _name = name;
       _id = id;
       _grade = grade;
}
  • Motivation: You have constructors on subclasses with mostly identical bodies

  • Mechanics: Create a superclass constructor; call this from the subclass methods.

public Manager (String name, String id, int grade) {
      super (name,id) ;
      _grade = grade;
}

Polimorfismo

Replace Conditional with Polymorphism
double getSpeed() {
   switch (_type) {
     case EUROPEAN:
         return getBaseSpeed() ;
     case AFRICAN:
         return getBaseSpeed() -getLoadFactor()  *
_numberOfCoconuts;
     case NORWEGIAN _BLUE:
         return  (_isNailed)  ? 0 : getBaseSpeed (_voltage) ;

   }
   throw new RuntimeException ("Should be unreachable") ;
}
  • Motivation: You have a conditional that chooses different behavior depending on the type of an object

  • Mechanics: Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract

Polymorphism
Replace Type Code with Subclasses
ReplaceTypeCodeWithSubclasses1
  • Motivation: You have an immutable type code that affects the behavior of a class

  • Mechanics: Replace the type code with subclasses

ReplaceTypeCodeWithSubclasses2
Replace Type Code with State/Strategy
Strategy1
  • Motivation: You have a type code that affects the behavior of a class, but you cannot use subclassing

  • Mechanics: Replace the type code with a state object

Strategy2
Replace Constructor with Factory Method
Employee (int type) {
    _type = type;
}
  • Motivation: You want to do more than simple construction when you create an object

  • Mechanics: Replace the constructor with a factory method

static Employee create (int type) {
    return new Employee (type);
}

Colaboraciones

Preserve Whole Object
int low = deysTempRange().getLow();
int high = deysTempRange() .getHigh();
withinPlan = plan.withinRange (low,high);
  • Motivation: You are getting several values from an object and passing these values as parameters in a method call.

  • Mechanics: Send the whole object instead

withinPlan = plan.withinRange(deysTempRange() );
Replace Delegation with Inheritance
ReplaceDelegationInheritance1
  • Motivation: You’re using delegation and are often writing many simple delegations for the entire interface.

  • Mechanics: Make the delegating class a subclass of the delegate

ReplaceDelegationInheritance2
Replace Inheritance with Delegation
ReplaceInheritanceDelegation1
  • Motivation: A subclass uses only part of a superclasses interface or does not want to inherit data

  • Mechanics: Create a field for the superclass, adjust methods to delegate to the superclass, and remove the subclassing

ReplaceInheritanceDelegation2
Hide Delegate
HideDelegate1
  • Motivation: A client is calling a delegate class of an object

  • Mechanics: Create methods on the server to hide the delegate

HideDelegate2
Remove Middle Man
RemuveMiddleMan1
  • Motivation: A class is doing too much simple delegation

  • Mechanics: Get the client to call the delegate directly

RemuveMiddleMan2
Change Unidirectional Association to Bidirectional
ChangeUnidirectionalAssociationBidirectional1
  • Motivation: You have two classes that need to use each other’s features, but there is only a one-way link.

  • Mechanics: Add back pointers, and change modifiers to update both sets

ChangeUnidirectionalAssociationBidirectional2
Change Bidirectional Association to Unidirectional
ChangeBidirectionalAssociationUnidirectional1
  • Motivation: [red]# You have a two-way association but one class no longer needs features from the other#

  • Mechanics: Drop the unneeded end of the association

ChangeBidirectionalAssociationUnidirectional2

Reparto de Responsabilidades

Move Method
moveMethod1
  • Motivation: A method is, or will be, using or used by more features of another class than the class on which it is defined.

  • Mechanics: Create a new method with a similar body in the class it uses most. Either turn the old method into a simple delegation, or remove it altogether.

moveMethod2
Move Field
moveField1
  • Motivation: A field is, or will be, used by another class more than the class on which it is defined.

  • Mechanics: Create a new field in the target class, and change all its users

moveField2
Inline Class
InlineClass1bis
  • Motivation: A class isn’t doing very much.

  • Mechanics: Move all its features into another class and delete it.

InlineClass1
Extract Class
ExtractClass1
  • Motivation: You have one class doing work that should be done by two

  • Mechanics: Create a new class and move the relevant fields and methods from the old class into the new class.

ExtractClass2
Replace Type Code with Class
ReplaceTypeCode1
  • Motivation: A class has a numeric type code that does not affect its behavior

  • Mechanics: Replace the number with a new class

ReplaceTypeCode2
Replace Array with Object
String [] row = new String[3];
row [0] ="Liverpool";
row [1] ="15";
  • Motivation: You have an array in which certain elements mean different things.

  • Mechanics: Replace the array with an object that has a field for each element

Performance row = new Performance ();
row.setName ("Liverpool");
row.setWins("15");
Replace Record with Data Class
  • Motivation: You need to interface with a record structure in a traditional programming environment.

  • Mechanics: Make a dumb data object for the record.

Introduce Parameter Object
InroduceParameterObject1
  • Motivation: You have a group of parameters that naturally go together

  • Mechanics: Replace them with an object.

InroduceParameterObject2
Replace Data Value with Object
ReplaceDataValue1
  • Motivation: You have a data item that needs additional data or behavior

  • Mechanics: Turn the data item into an object.

ReplaceDataValue2
Change Value to Reference
ChangeValueReference1
  • Motivation: You have a class with many equal instances that you want to replace with a single object

  • Mechanics: Turn the object into a reference object.

ChangeValueReference2
Change Reference to Value
ChangeReferenceValue1
  • Motivation: You have a reference object that is small, immutable, and awkward to manage

  • Mechanics: Turn it into a value object

ChangeReferenceValue2

Bibliotecas & GUI

Introduce Foreign Method
Date newStart =new Date (previousEnd.getYear(),
              previousEnd.getMonth(), previousEnd.getDate() +1);
  • Motivation: A server class you are using needs an additional method, but you can’t modify the class.

  • Mechanics: Create a method in the client class with an instance of the server class as its first argument

Date newStart =nexDay(previousEnd);

private static Date nextDay(Date arg) {
    return new Date (arg.getYear(),arg.getMonth(),arg.getDate() +1);
    }
Introduce Local Extension
IntroduceLocalExtension1
  • Motivation: A server class you are using needs several additional methods, but you can’t modify the class.

  • Mechanics: Create a new class that contains these extra methods. Make this extension class a subclass or a wrapper of the original

IntroduceLocalExtension2
Duplicate Observed Data
DuplicateObservedData1
  • Motivation: You have domain data available only in a GUI control, and domain methods need access.

  • Mechanics: Copy the data to a domain object. Set up an observer to synchronize the two pieces of data

DuplicateObservedData2

Granularidad del Refactoring

Característica Refactoring de Catálogo Refactoring en Grande
  • Precisión

Alto, añadir un parámetro, …​

Baja, desenmarañar una jerarquía de herencia, …​

  • Duración

Minutos, …​ hora

Meses…​, años, todos los días un poquito!!!

  • Progreso

Visible

Difuso

  • Participante

Individual

Equipo coordinado con un mismo objetivo de diseño, evitando pisar lo barrido!!!

  • Sensación

Satisfacción instantánea

Vivir tranquilo, no grandes logros

Ejemplo de refactoring en grande

  • Falta de cohesión, mezclando el modelo y la vista de presentación

    • Trato, clase base de la jerarquía

    • Trato activo y pasivo, con estilo de presentación simple

    • Trato activo y pasivo tabulares, con estilo de presentación tabular

Deal1
Deal2
Deal3
Deal4
Deal5

Ejemplo movies

Versión 1.

Clase “Customer” - Método “statement()”
movies1
  • Smell code :

    • Método largo ⇒ más de 15 líneas

      • Mal reparto de responsabilidades ⇒ Falta de Cohesión

      • Imposible reutilizar para otro formato (HTML) ⇒ Inmóvil

        • copy+paste ⇒ DRY

    • Cambio de política de coste y puntos ⇒ Viscoso

      • copy+paste ⇒ multiplica y complica el mantenimiento

  • Refactoring :

    • Extraer Método ⇒ switch y código dependiente en método “amountFor” de clase Customer

  • Producción

  • Prueba

Versión 2.

Clase “Customer“ - Método “amountFor()“
movies2
  • Smell code :

    • Malos nombres ⇒ “each” y “thisAmount”

  • Refactoring :

    • Renombrar variable ⇒ “rental” y “result”

  • Producción

  • Pruebas

Versión 3.

Clase “Customer“ - Método “amountFor()“
  • Smell code :

    • Envidia de características ⇒ get(), get(), … desde el servidor

    • Clase de Datos ⇒ get(), get(), … en el cliente

    • Experto en Información ⇒ la clase responsable es la que tiene la información

  • Refactoring :

    • Mover método ⇒ método “getCharge” a clase “Rental

  • Producción

  • Pruebas

Versión 4.

Clase “Customer“ – Método “amountFor()“
movies4
  • Smell code :

    • “Innecesaria descomposición”: método privado sin justificación

  • Refactoring :

    • Método en línea ⇒ eliminar “amountFor”

  • Producción

  • Pruebas

Versión 5.

Clase “Customer“ – Método “statement()“
movies5
  • Smell code :

    • Variable temporal innecesaria ⇒ “thisAmount”

  • Refactoring :

    • Reemplazar Temporal por Consulta ⇒ “each.getCharge()”

  • Producción

  • Pruebas

Versión 6.

Clase “Customer“ – Método “statement()“
  • Smell code :

    • Método largo ⇒ más de 15 líneas

  • Refactoring :

    • Extraer método ⇒ método “getFrequentRenterPoints()“ a clase “Rental”

  • Producción

  • Pruebas

Versión 7.

Clase “Customer“ – Método “statement()“
movies7
  • Smell code :

    • Variable temporal innecesaria ⇒ “totalAmount”

  • Refactoring :

    • Reemplazar Temporal por Consulta ⇒ “this.getTotalCharge()”

  • Producción

  • Pruebas

Versión 8.

Clase “Customer“ – Método “statement()“
movies8
  • Smell code :

    • Variable temporal innecesaria ⇒ “frequentRenterPoints”

  • Refactoring :

    • Reemplazar Temporal por Consulta ⇒ “this.getTotalFrequentRenterPoints()”

  • Producción

  • Pruebas

Versión 9.

Clase “Rental“ – Método “getCharge()“
movies9
  • Smell code :

    • Experto en la Información ⇒ la mayor parte de la información es de Movie

  • Refactoring :

    • Mover Método ⇒ método “getCharge()” a clase “Movie”

  • Producción

  • Pruebas

Versión 10.

Clase “Rental“ – Método “getFrequentRenterPoints()“
movies10
  • Smell code :

    • Experto en la Información ⇒ la mayor parte de la información es de Movie

  • Refactoring :

    • Mover Método ⇒ método “getFrequentRenterPoints()” a clase “Movie”

  • Producción

  • Pruebas

Versión 11.

Clase “Movie“ – Método “getCharge()“
movies11
  • Smell code :

    • Sentencia Alternativa Múltiple ⇒ el comportamiento depende de un tipo/código/…​

  • Refactoring :

*Reemplazar Código de Tipo con Estrategia/Estado (inyección de dependencias) ⇒ Auto-encapsular campo ⇒ “priceCode”

Versión 12.

Clase “Movie“ – Método “getCharge()“
  • Smell code :

*Sentencia Alternativa Múltiple ⇒ el comportamiento depende de un tipo/código/…​

  • Refactoring :

    • Reemplazar Código de Tipo con Estrategia/Estado (inyección de dependencias) ⇒ Añadir nuevas clases ⇒ “Price”, “ChildrenPrice”, …​

  • Producción

  • Pruebas

Versión 13.

Clase “Movie” - Atributo “priceCode”
movies13
  • Smell code :

    • Sentencia Alternativa Múltiple ⇒ el comportamiento depende de un tipo/código/…​

  • Refactoring :

    • Reemplazar Código de Tipo con Estrategia/Estado (inyección de dependencias) ⇒ Sustituir atributo: “int priceCode” por “Price price”

  • Producción

  • Pruebas

Versión 14.

Clase “Movie” - método “getCharge()”
movies14
  • Smell code :

    • Sentencia Alternativa Múltiple ⇒ el comportamiento depende de un tipo/código/…​

  • Refactoring :

    • Reemplazar Código de Tipo con Estrategia/Estado (inyección de dependencias) ⇒ Mover método: método “getCharge()” a Clase “Price”

  • Producción

  • Pruebas

Versión 15.

Clase “Price” - método “getCharge()”
movies15
  • Smell code :

    • Sentencia Alternativa Múltiple ⇒ el comportamiento depende de un tipo/código/…​

  • Refactoring :

*Reemplazar Condicional con Polimorfismo ⇒ Redefinir método “getCharge()” en clases derivadas

Versión 16.

Clase “Price” - método “getFrequentRenterPoints()”
movies16
  • Smell code :

    • Sentencia Alternativa Múltiple ⇒ el comportamiento depende de un tipo/código/…​

  • Refactoring :

*Reemplazar Condicional con Polimorfismo ⇒ Redefinir métod“getFrequentRenterPoints()” en clases derivadas

Versión 17.

Clase “CustomerTest” - métodos de prueba con Tipo/Código
movies17
  • Smell code :

    • Sentencia Alternativa Múltiple ⇒ el comportamiento depende de un tipo/código/…​

  • Refactoring :

  • Ocultar campo: añadir métodos “childrens()”, “regular()” y “newRelease()” en clase “MovieBuilder”

  • Producción

  • Pruebas

Versión 18.

Clase “CustomerTest” - métodos de prueba con Tipo/Código
  • Smell code :

    • Sentencia Alternativa Múltiple ⇒ el comportamiento depende de un tipo/código/…​

  • Refactoring :

    • Ocultar campo: desde “CustomerTest” llamar a métodos “childrens()”, “regular()” y “newRelease()” de clase “MovieBuilder”

    • Sustituir campo: atributo “priceCode” por “Price” en clase “MovieBuilder”

    • Sustituir campo: atributo “priceCode” por “Price” en clase “Movie”

  • Producción

  • Pruebas

Versión 19.

Clase “Price” - método “getPriceCode()”
  • Smell code :

    • Sentencia Alternativa Múltiple ⇒ el comportamiento depende de un tipo/código/…​

  • Refactoring :

    • Eliminar método: “getPriceCode()” de clases “Price” y “Movie”

  • Producción

  • Pruebas

Versión 20.

Jerarquía “Price”
movies20
  • Smell code :

    • Números mágilos ⇒ 2, 1.5, …​

  • Refactoring :

    • Añadir atributo: “CHARGE”, “DAYS_RENTER_THRESHOLD”, …​ en jerarquía “Price”

  • Producción

  • Pruebas

Versión 21.

movies21

Sintesis

Bibliografía

Obra, Autor y Editor Portada Obra, Autor y Editor Portada
  • Refactoring (Pearson Addison-Wesley Signature Series)

    • Fowler Martin

    • Financial Times Prentice Hall; Edición: 01 (28 de noviembre de 2018)

height32

  • Test Driven Development. By Example (The Addison-Wesley Signature Series

    • Beck Kent

    • Addison Wesley; Edición: 01 (8 de noviembre de 2002)

height32

  • Extreme Programming Explained: Embrace Change: Embracing Change

    • Kent Beck, Cynthia Andres

    • Addison-Wesley Educational Publishers Inc; Edición: 2nd edition (16 de noviembre de 2004)

height32

  • Planning Extreme Programming (The Xp Series)

    • Beck Kent, Fowler Martin

    • Addison Wesley; Edición: 01 (16 de octubre de 2000)

height32

  • Scrum and XP from the Trenches (Enterprise Software Development)

    • Henrik Kniberg

    • Lulu.com; Edición: 1 (4 de octubre de 2007)

height32

  • Scrum: El revolucionario método para trabajar el doble en la mitad de tiempo

    • Jeff Sutherland,

    • Editorial Ariel (20 de enero de 2015)

height32

  • Refactoring Databases: Evolutionary Database Design

    • Scott J Ambler, Pramod J. Sadalage

    • Addison-Wesley Professional; Edición: 1 (13 de marzo de 2006)

height32

  • User Stories Applied: For Agile Software Development

    • Cohn, M.

    • Addison Wesley, 2004

UserStoriesAppliedForAgileSoftwareDevelopment

  • Scrum y XP desde las trincheras

    • Henrik Kniberg. Prólogos de Jeff Sutherland y Mike Cohn

    • proyectalis.com

ScrumXPTrenches

  • Diseño Ágil con TDD

    • Carlos Blé Jurado

    • Lulu.com

DisenoAgilconTDD

Ponente

  • Luis Fernández Muñoz

setillo
  • Doctor en Inteligencia Artificial por la UPM

  • Ingeniero en Informática por la UMA

  • Diplomado en Informática por la UPM

  • Profesor Titular de ETSISI de la UPM