viernes, 26 de agosto de 2011

lecturas que voy haciendo

http://www.slideshare.net/fungfung/refactoringch7-moving-feature-btw-objects
http://www.source-code.biz/snippets/java/3.htm, just use vector f.e.
http://www.ensta-paristech.fr/~diam/java/online/notes-java/data/expressions/22compareobjects.html
http://pivotallabs.com/users/alex/blog/articles/273-lovely-demeter-meter-maid, LOL , al final se calienta el tema
http://www.purpletech.com/blog/index.php?itemid=25
http://moffdub.wordpress.com/2008/09/27/domain-driven-method-naming-guidelines/#comment-236

Test Driven Develpment SMELL thoughts

Cuando enuncio un test , y luego desarrollo el código para conseguir ponerlo en verde , veo que al poner assertions de tipo TRUE FALSE, el error no me escupe el resultado que comparo, mientras que si pongo un equals si que me lo da.
SMELL -> assertsTrue and assertsFalse use.
GOOD PRACTICE -> no uses assertions tipo True/false mas que como último remedio.

Otro problema que me he encontrado es que a veces para depurar el código y ver la razón por la que no me ha funcionado un test, me veo obligado a poner salidas por el stdout que me muestren algún valor de variables en el código, o en el test.
Si te ves obligado a ello , probablemente debes pensar primero si tu test es lo suficiente unitario, "solo probar una funcionalidad cada vez", o bien si en los métodos de tu código estas necesitando extract type refactoring (class, method, field).
SMELL -> logs, debug intermediate variables, system.out.println, .......
GOOD PRACTICE -> reescribe el código o crea un nuevo test antes de usar logs para depurar el código.

En algunos al escribir un test sobre una funcionalidad complicada me veo perdido pues para poner verde el test debo de programar mucho código con muchos methodos privados. Normalmente esto metodos lo que hacen es calcular datos necesarios o validar variables, hay que ver los métodos y clases involucradas y usar el fallback de ellas para probar cada una de las opciones , y expander el test haciendolo mas vervose y probando cada una de las opciones. Por ejemplo metodos que devuelvan un booleano ,pensar un test que pruebe el resultado de la funcionalidad completa con ese booleano a false, y luego en true, asi con todo.
SMELL -> too many methods or classes creates for acomplish a test. Need to change the scope of a method to public , just to test it.
GOOD PRACTICE -> Split test in each case depending on secundary methods results.

miércoles, 24 de agosto de 2011

Test Driven Develpment Issues in refactoring

I am practicing with the Kata minesWeesper, an in the development of the code i have introduce a Class that parses the file.
When that Class parses and find a pair of numbers valid (see kata doc) it returns that pair.
First i was coding that pair as int[2], in order to make an extract class refactoring when the code was write, so my methods like getNextDimensions() returns a int[2].
I wrote the test in Junit and in order to check the answer i had compared to a new int[2]= [expectedResultLines,expecterResulColumns].
Once all my test pass, i began to refactor code. (this could be a point of my error, but not the clue), when i did the refactor, i created a class MapDimensions , which stores the dimensions of the map in two variables.
And now a find me changing all the test and the clients of my class.

How i could avoid that mistake?

I don't know just now but i can expose some clues.
SMELL.- test comparing primitive results.--> change to a class container.
think also to use encapsulate field
Good practice --> introduce name of the expected type of answer in test name
introduce "expected" word in the declaration of the expected result to mach in the test.

jueves, 18 de agosto de 2011

Test System.out.println() Junit

Durante la realizacion de una kata
minesweeper
Me surgió la implementación de una clase que mantiene los mapas de minas, y que debe tener un método publico el cual imprime el mapa de minas.
al principio lo que hice fué crear un metodo tambien publico llamado expresate() que devolvia la cadena que luego se imprimiria mediante un System.out.println(), sin embargo esto lo que suponia era la exposicion de una metodo en el api que no tenia porque ser publico.
al refactorizar y hacer los metodos privados excepto los necesarios para el api, me encontraba con el problema de comprobar que es lo que sale por el stdOut
esto lo solucioné mirando en una pagina que me indicaba como hacerlo así que ...
antes tenia:

package buscaminas;

import junit.framework.TestCase;

public class MapaMinasTest extends TestCase {
public void testProcesaLineaVacia() {
String lineaAProcesar = "......";
int ordenTablero = lineaAProcesar.length();
MapaMinas testMapa = new MapaMinas(1,ordenTablero);
testMapa.procesaLinea(0, lineaAProcesar);
String resultado=testMapa.expresate(0);
assertEquals("000000", resultado);
}

y ahora:

package buscaminas;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import junit.framework.TestCase;

public class MapaMinasTest extends TestCase {
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();


public void testProcesaLineaVacia() {
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));

String lineaAProcesar = "......";
int ordenTablero = lineaAProcesar.length();
MapaMinas testMapa = new MapaMinas(1,ordenTablero);
testMapa.procesaLinea(0, lineaAProcesar);
testMapa.imprime();

assertEquals("000000\r\n", outContent.toString());
System.setOut(null);
System.setErr(null);

}


notese adema de las variables creadas y el método llamado, el comparador:

assertEquals("000000", resultado);
assertEquals("000000\r\n", outContent.toString());

se debe introducir el retorno de carro y el final de linea del println() que hay en el código del método imprimete()

miércoles, 17 de agosto de 2011

File Read good Practice

Muchas veces nos encontramos ante la lectura de un fichero que contiene datos que nuestro programa debe procesar.
Seguro que existen muchas "recomendaciones" al respecto pero yo no encuentro ninguna ahora así que voy a poner lo que me parece hoy por hoy y según se amplíe mi conocimiento y experiencia lo modificaré.
1.- ¿Comprobamos que existe el fichero y su integridad?
los siguientes puntos pueden depender de si una información posterior en el fichero puede alterar el proceso de información anterior. pero se trata de tener una norma genérica.
2.- ¿leemos los datos de golpe o secuencialmente linea tras linea?
3.- ¿procesamos los datos según los estamos leyendo o los almacenamos en memoria?
4.- ¿si los almacenamos , liberamos la memoria al final?


ventajas de guardar el puntero a donde estamos leyendo en un variable global
ventajas de leer todo

lunes, 15 de agosto de 2011

replace conditional with polymorphism

Este metodo de refactorizacion ha ido cambiando , he empezado a trabajar con el ejemplo que se puede ver en Refactorización: Reemplazar un condicional por polimorfismo
y he acabado implementando un factory patter al estilo de la web
el codigo inicial era , completado para que funcione.

double final MAX_VEHICLE_SPEED = 18;
double final TRUKC_LOAD_FACTOR = 28;
double final BUS_SAFETY_FACTOR = 44;
double loadFactor;
double safetyFactor;

double getSpeed(){

_type= "CAR";
_load = 12;
_risk = 3;
setLoadFactor (TRUCK_LOAD_FACTOR);
setSafetyFactor (BUS_SAFETY_FACTOR);

switch (_type) {
case CAR:
return getMaxSpeed();
case TRUCK:
return getMaxSpeed() - getLoadFactor()* _load;
case BUS:
return getMaxSpeed() - getSafetyFactor()*_risk;
}
double getMaxSpeed()
{
return MAX_VEHICLE_SPEED;
}
private setLoadFactor(Double factor)
{
loadFactor = 0;
if (factor) loadFactor = factor;
}
double getLoadFactor()
{
return loadFactor;
}
private setSafetyFactor(Double factor)
{
safetyFactor = 0;
if (factor) safetyFactor = factor;
}
double getSafetyFactor()
{
return safetyFactor;
}

esto pasa de la forma que se puede observar en el archivo de MoonEdit

Class Cliente
{
private Integer _load;
private Integer _risk;
privat String _type;

double getSpeed()
{
_load = 12;
_risk = 3;
return new VehicleFactory(_type,_load,_risk).getVehicle().getSpeed(); //smell
}
}

Class VehicleFactory
{
final String CAR = "car";
final String BUS = "bus";
final String TRUNCK = "truck";

getVehicle(String type, Integer load , Integer risk)
{
switch (type)
{
case CAR:
return new Car();
case TRUCK:
return new Truck(load);
case BUS:
return new Bus(risk);
}
return new Exception ("Vehiclefactory","no vehicle type");
}
}

Class abstract Vehicle
{
private integer _maxSpeed;

private Integer getMaxSpeed()
{
return _maxSpeed;
}
private setMaxSpeed(Integer speed)
{
_maxSpeed = speed;
}
public abstract Double getSpeed()
}

Class Car extends Vehicle
{
private final Integer CAR_MAX_SPEED = 18;

public Car()
{
setMaxSpeed(CAR_MAX_SPEED);
}

public Double getSpeed()
{
return getMaxSpeed();
}
}

Class Truck
{
private final Integer TRUCK_MAX_SPEED = 18;
private final Integer TRUCK_LOAD_FACTOR = 28;
private Integer _loadFactor;
private Integer _load;

public Truck(Integer load)
{
setMaxSpeed(TRUCK_MAX_SPEED);
setLoadFactor(TRUNK_LOAD_FACTOR);
if (load) _load = load else load = 0;
}
public Integer getLoadFactor()
{
return _loadFactor;
}
private setLoadFactor (Integer loadFactor)
{
_loadFactor = loadFactor;
}
private Double getSpeed()
{
return getMaxSpeed() + getLoadFactor() * _load;
}
}

Class Bus
{
private final Integer BUS_MAX_SPEED = 18;
private final Integer BUS_SAFETY_FACTOR = 44;
private Integer _safetyFactor;
private Integer risk;

public Bus(Integer risk)
{
setMaxSpeed(BUS_MAX_SPEED);
setSafetyFactor (BUS_SAFETY_FACTOR);
if (risk) _risk = risk else _risk =0; ahora luego hago los seters para ponerlo bien.
}
public Integer getsafetyFactor()
{
return _safetyFactor;
}
private setSafetyFactor (Integer safetyFactor)
{
_safetyFactor = safetyFactor;
}
private abstract Double getSafetySpeed()
{
return getMaxSpeed() - getSafetyFactor() * risk;
}
private Double getSpeed()
{
return getSafetySpeed();
}
}

domingo, 7 de agosto de 2011

flash cards refactoring

Aquí tengo subidas unas cuantas flashcards para ayudar a prenderme todos los refactorings que hay y las familias a las que pertenecen, así como códigos de ejemplo para aprender a identificar el refactoring que hay que hacer.
flashcards sobre refactoring

sábado, 6 de agosto de 2011

Replace Parameter with Method

el proceso entero con comentarios se puede seguir mediante el archivo de MoonEdit (programa muy chulo , gratis y sin peso apenas) replace parameter with method
CODIGO INICIAL
public double getPrice()
{
int basePrice = _quantity * _itemPrice;
int discountLevel;
if (_quantity > 300) discountLevel = 3;
else if (_quantity > 200) discountLevel = 2;
else discountLevel = 1;
double finalPrice = discountedPrice (basePrice, discountLevel);
return finalPrice;
}

private double discountedPrice (int basePrice, int discountLevel)
{
if (discountLevel == 2) return basePrice * 0.1;
elseif (discountLevel == 3) return basePrice * 0.2;
else return basePrice * 0.05;
}


CODIGO FINAL
public double getPrice()
{
if (Discount.isEnoughQuantityForPlatinum(_quantity))
return getBasePrice() * Discount.PLATINUM.getPercentage();
elseif (Discount.isEnoughQuantityForPlus(_quantity))
return getBasePrice() * Discount.PLUS.getPercentage();
else
return getBasePrice() * Discount.NORMAL.getPercentage();
}
private int getBasePrice()
{
return _quantity * _itemPrice;
}
class Discount
{
static final int MIN_QUANTITY_FOR_PLATINUM = 300;
static final int MIN_QUANTITY_FOR_PLUS = 200;

static final int PLATUNUM_LEVEL = 3;
static final int PLUS_LEVEL = 2;
static final int NORMAL_LEVEL = 1;
static final float PLATINUM__PERCENTAGE = 0.2;
static final float PLUS__PERCENTAGE = 0.1;
static final float NORMAL_PERCENTAGE = 0,05;
public static final Discount NORMAL = new Discount (NORMAL_LEVEL , NORMAL_PERCENTAGE);
public static final Discount PLUS = new Discount (PLUS_LEVEL , PLUS_PERCENTAGE);
public static final Discount PLATINUM = new Discount (PLATINUM_LEVEL , PLATINUM_PERCENTAGE);
private final int _level;
private final float _percentage;

private Discount(int level,float percentage)
{
_level = level;
_percentage = percentage;
}
public getLevel()
{
return _level;
}
public getPercentage()
{
return _percentage;
}
public boolean isEnoughQuantityForPlus(quantity)
{
if (quantity > MIN_QUANTITY_FOR_PLUS) return true;
return false;
}
public boolean isEnoughQuantityForPlatinum(quantity)
{
if (quantity > MIN_QUANTITY_FOR_PLATINUM) return true;
return false;
}


bad Smell: getPrice() -- long method , finalPrice variable is always the discount Calculated Price
refactoring used :
replace parameter with method <- making methods calls simpler
invented name refactor (i don´t know if its a current name for this)
introduce multible return points <- simplifying Conditional Expressions
inline method <- componsing methods
replace Magic Number with Symbolic constant <- organizing data
replace type code with class <- organizing data
move method <- move features between objects
consolidate conditional expression <- simplifying conditional expressions

jueves, 4 de agosto de 2011

Preserve Whole Object exception

He tratado de realizar variaciones sobre la posibilidad que se apunta en "sourcemaking.com":
[motivation] "Passing in the required object causes a dependency between the required object and the called object. If this going to mess up your dependency structure, don't use Preserve Whole Object".
Como excepción a la aplicación de preserve whole object<-making methods caller sympler.
para ello he usado un código inventado por mi a bote pronto, sobre la gestión de una película y la contabilidad de sus gastos quedando el código de la forma siguiente:
se puede descargar el archivo de moonEdit para ver como he llegado a ese código y consideraciones preserver whole object exception
class peliculaDeMiedo
{
main
Camara camara = new Camara(4);
integer Plano = 6;
integer escena = 8;
integer litrosSangre= 44;
String [] actores = [hulbrainer -> datosHulbrainer, kevincosnerjose ->datosKevincosnerjose, avaganer->datosAvaganer];
Vampiro dragucula = new Vampiro(camara,plano,escena,actores[hulbrainer]);
...
...
ElementosPelicula elementosUsados = new ElementosPeliculaMiedo(camara,plano,escena,litrosSangre);
Double pagaDia = new ContableMiedo().dameLaPagaStaff(elementosUsados);
}

class ElementosPelicula
class ElementosPeliculaAmor extends ElementosPelicula
class ElementosPeliculaMiedo extends ElementosPelicula


class Camara
{
Camara(integer camara)
{
un monton de peticiones a otros metodos, consultas a servicios y logaritmos nepericonianos;
this.ordenDeLaCamara=camara;
}
Double calculaCosteAnual()
{
un monton de calculos y de historias que miran el precio de la camara , depreciacion , duracion de la lampara y lo que te de la gana
}
}

class Vampiro
Vampiro (Camara CamaraEnfocada, integer PlanoPelicula, integer escena,Actor actor)
{
sus cosas
}

class Contable
{
Contable()
{
se hacer cositas con los números
}
public Double damePagaStaff (elementosPelicula elementos)
{
Double consteCamara = elementos.camara.calcilaCosteAnual()/365;
double gastoExtraPelicula = calculaGastoProducto(elementos);
etc etc....
return unapasta;
}
private abstract double calculaGastoProducto(ElementosPelicula elementos);
}

class ContableMiedo extends Contable
calculaGastoProducto(elementos)
{
return elementos.litrosSangre*getPrecioMercadoEuros();
}

class ContableAmor extends Contable
calculaGastoProducto(elementos)
{
return elementos.kilosCebolla*getPrecioMercadoEuros();
}


class peliculaDeAmor
{
main
Camara camara = new Camara(4);
integer Plano = 6;
integer escena = 8;
integer kilosCebolla= 4;
String [] actores = [hulbrainer -> datosHulbrainer, kevincosnerjose ->datosKevincosnerjose, avaganer->datosAvaganer];
...
...
ElementosPelicula elementosUsados = new ElementosPeliculaAmor(camara,plano,escena,kilosCebolla)
Double pagaDia = new ContableAmor().dameLaPagaStaff(elementosUsados);
}

extract method

Estas son una serie de consideraciones sobre el código de ejemplo de Extract method de la web de sourcemaking.com

void printOwing(double previousAmount)
{
Enumeration e = _orders.elements();
double outstanding = previousAmount * 1.2;

// print banner
System.out.println ("**************************");
System.out.println ("***** Customer Owes ******");
System.out.println ("**************************");

// calculate outstanding
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
}

//print details
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);
}

que pasa de la forma que se puede ver en el archivo de moonEdit extract method archive
a ser el siguiente código


void printOwing(double previousAmount)
{
printBanner();
printDetails(calculateOutstanding(previusAmount*1.2));
}
void printBanner()
{
System.out.println ("**************************");
System.out.println ("***** Customer Owes ******");
System.out.println ("**************************");
}
void printDetails(Double outstandingArg)
{
System.out.println ("name:" + _name);
System.out.println ("amount" + outstandingArg);
}
Double calculateOutstanding(Double previousValue)
{
Double result = previousValue;
Enumeration e = _orders.elements();
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding result += each.getAmount();
}
return result;
}

lunes, 1 de agosto de 2011

Reflexiones aplicación refactoring

A partir del pseudo código de ejemplo de Form Template Method, de la web sourcemaking.com/refactoring se me planteó la duda sobre como hacer que este código funcione, y esto es lo que sale, con ejemplos de uso de :
para ver paso a paso como lo he hecho puedes descargar este archivo de MoonEdit form template method.me
pseudo código Original
en el que expone el problema para usar Form Template Method.

class Site
class Residential extends Site

public Double getBillableAmount()
{
return double base=_units*_rate;
double tax=base * Site.TAX_RATE;
return base+tax;
}

class Lifeline extends Site
public Double getBillableAmount()
{
double base=_units*_rate * 0,5;
double tax=base * Site.TAX_RATE*0,2;
return base+tax;
}
Proponiendo en su portal esta solución:

class Site
function Double getBillableAmount()
{
return getBaseAmount() + getTaxAmount();
}
funtion Double abstract getBaseAmount();
funcion Double abstract getTaxAmount();


class Residential extends Site
function Double getBaseAmount()
{
Double base= _units*_rate;
return base;
}
funtion Double getTaxAmount()
double tax=base * Site.TAX_RATE;
return tax;
}

class Lifeline extends Site
function Double getBaseAmount()
{
Double base= _units*_rate*0,5;
return base;
}
funtion Double getTaxAmount()
double tax=base * Site.TAX_RATE*0,2;
return tax;
}

utilizando las refactorizaciones:

  1. replace temp with query

  2. pull up method

  3. form template method

  4. pull up field

  5. self encapsulate field

  6. lazy initialization


llego al siguiente código (tambien añadida la especificacion de que base requiera un importante cálculo para su obtención)

// Refactoring about Form template method code in sourcemaking.com
class Site
{
Double base;
public Site()
{
setBase(undefined);
}
private Double getBase()
{
return this.base();
}
private setBase (Double base)
{
this.base = getBaseAmount();
}
public Double getBillableAmount()
{
return getBaseAmount()+ getTaxAmount();
}

private Abstract getBaseAmount();
private Abstract getTaxAmount();
}

class Residential extends Site
{
private Double getBaseAmount()
{
Double result = espensive calculus;
setBase(result);
return result;
}
private Double getTaxAmount()
{
if (!defined(getBase()) getBaseAmount();
return getBase() * Site.TAX_RATE;
}
}

class Lifeline extends Site
{
private Double getBaseAmount()
{
Double result = espensive diferent calculus;
setBase(result);
return result;
}
private Double getTaxAmount()
{
if (!defined(getBase()) getBaseAmount();
return getBase() * Site.TAX_RATE*0,2;
}
}

//SMELL : similar code in two subclases
//replace temp with query<- composing methods
//pull up method <- Dealing with Generalizaton
//form template method <- Dealing with Generalization
//pull up field <- Dealing with generalization
//self Encapsulate Fiel <- Organizing data
//lazy initialization



bad Smell, cosas que me huelen mal del código y que aún no tengo claro el refactoring:
  1. la linea if(!defined(getBase())

    • ¿a que igualo this.base en la inicialización? a undefined, a null, a 0, a DOUBLE_NULL_VALUE