refactoring workshop - rubyconf argentina 2014

Post on 08-Jul-2015

388 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Slidedeck for the Refactoring Workshop given at RubyConf Argentina 2014 rubyconfargentina.org

TRANSCRIPT

4 recetas para simplificar tu código

git clone http://github.com/tute/refactoring-workshop

¡Buen día!

1. ¿Cómo te llamás? 2. ¿Sos nuevo/nueva programando? 3. ¿Cuánto tiempo llevás programando en Ruby?

Aprenderemos a transformar esto…

En esto!

Patrones de Refactorización

Eufemismo para “No vas a creer cómo simplificamos

esta masa de código en 5 sencillos pasos”

Hoy veremos

Sobre mi

Sobre mi

Sobre mi

Sobre mi

Sobre mi

http://www.nodocomunitario.org/?page_id=462

Sobre mi

http://www.nodocomunitario.org/?page_id=462

Sobre mi

http://www.nodocomunitario.org/?page_id=462

Sobre mi

2011/2012: Chef Surfing

• No parábamos de programar

• La complejidad nos bloqueaba c/ 2 meses

• Parábamos. Testéabamos. Refactorizábamos.

• No era predecible.

Sobre mi

2011/2012: Chefsurfing

Sobre mi

2013: General Assembly

• Rails app en malas condiciones

• Pero con buenos tests

• Me dijeron “refactorizá lo que quieras”

• Agilizó el trabajo, semana tras semana

• Quiero repetir (y mejorar) esta historia

Sobre mi

2013: General Assembly

Sobre mi

2014: thoughtbot

• Calidad del código es el pan de cada día

• Priorizamos agresivamente qué desarrollar

• Escribimos lo mejor posible siempre

• No se acepta deuda técnica

• Programadores y clientes felices

Sobre mi

2014: thoughtbot

Patrones de Refactorización

1. Método que revela la intención

2. Objeto de "Caso Especial"

3. Reemplazar método con Objeto Método

4. Objeto "Servicio"

Hoy veremos

1/4 Método que revela la intención

Método que revela la intención

Porqué se llama es más importante que qué, o cómo, se hace.

Método que revela la intención

1/4 Método que revela la intención

“Planché la camisa y me afeité” (qué)

“Puse la camisa sobre la remera que me regaló mi tía, y pasé una afeitadora por mi cara” (cómo)

“Me preparé para salir” (porqué)

1/4 Método que revela la intención

# Remove duplicates?if hash[row[1]][date] != row[0] # ...

if remove_duplicates?(row, date) # ...

def remove_duplicates?(row, date) hash[row[1]][date] != row[0]

Un Algoritmo

1/4 Método que revela la intención

1. Agregar comentarios si son necesarios

2. Transformar comentarios en métodos

3. Los comentarios son ahora código

4. El código se auto-describe

¿“No más de 5 líneas por método”?

1/4 Método que revela la intención

¡No hay problema!

El patrón más sencillo

1/4 Método que revela la intención

y difícil a la vez

Sin Tests no hay refactorización

O van a tener que escuchar, con razón, “no arregles lo que no está roto”

Prerequisito: hay tests

Prerequisito: hay tests

¡Estos karting van a 200!

Prerequisito: hay tests

Parece que este 747 no se mueve.

Sens

ació

n de

Vel

ocid

ad

0

50

100

150

200

Velocidad Real

0 250 500 750 1000

747

Kart

Prerequisito: hay tests

Prerequisito: hay tests

Sens

ació

n de

Vel

ocid

ad

0

50

100

150

200

Velocidad Real

0 250 500 750 1000

TDD-refactor

Deploy!

Objeto de Caso Especial (Nulo)

2/4 Objetos de Caso Especial

El objeto más común del mundo Ruby:

nil

Problema 1: origen de nil

2/4 Objetos de Caso Especial

session[:current_user] # => nil session[:current_uzer] # => nil if (false) then 1 end # => nil empty_method() # => nil

Problema 1: origen de nil

2/4 Objetos de Caso Especial

Undefined method `title' for nil:NilClass

¿Viene de un blog post, un producto, o una noticia?

2/4 Objetos de Caso Especial

def blog_post Post.find(params[:id]) || :no_post end blog_post.title

Undefined method `title' for no_post:Symbol

Problema 1: mejor retornar un símbolo

Problema 2: condicionales

2/4 Objetos de Caso Especial

if blog_post “Ver #{blog_post.title}” else ‘Ver todos los posts' end

Más imperativo que orientado a objetos.

Fácil de olvidar.

Solución: retornar un Objeto

2/4 Objetos de Caso Especial

class NullPost def title; “todos”; end end

def blog_post Post.find(params[:post_id]) || NullPost.new end

“Ver #{blog_post.title}” # WIN

3/4 Reemplazar Método con Objeto Método

def row_per_day_format(file_name) file = File.open file_name, 'r:ISO-8859-1' # hash[NivelConsistencia][date] = [[value, status]] hash = { '1' => {}, '2' => {} } dates = [] str = ''

CSV.parse(file, col_sep: ';').each do |row| next if row.empty? next if row[0] =~ /^\/\// date = Date.parse(row[2]) (13..43).each do |i| measurement_date = date + (i-13)

# If NumDiasDeChuva is empty it means no data

3/4 Reemplazar Método con Objeto Método

Reemplazar Método con Objeto Método

1. Crear una clase con los mismos argumentos de inicialización que el método

2. Copiar y pegar el método en la nueva clase, sin argumentos

3. Reemplazar el método original con un llamado a la nueva clase

4. Aplicar “Método que Revela la Intención”. Voilà.

1. Nueva clase con mismos argumentos

class FormatAtoB def initialize(file_name) @file_name = file_name end end

3/4 Reemplazar Método con Objeto Método

2. Copiar y pegar el cuerpo

class FormatAtoB def initialize(file_name) @file_name = file_name end

def row_per_day_format file = File.open file_name, 'r:ISO-8859-1' # …

3/4 Reemplazar Método con Objeto Método

3. Reemplazar llamada original

def row_per_day_format(file_name) FormatAtoB.new(file_name). row_per_day_format end

3/4 Reemplazar Método con Objeto Método

4. Aplicar “Método que Revela la Intención”

class FormatAtoB def initialize(file_name) @file_name = file_name end

def row_per_day_format load_file_a format_data end …

3/4 Reemplazar Método con Objeto Método

3/4 Reemplazar Método con Objeto Método

Reemplazar Método con Objeto Método

1. Crear una clase con los mismos argumentos de inicialización que el método

2. Copiar y pegar el método en la nueva clase, sin argumentos

3. Reemplazar el método original con un llamado a la nueva clase

4. Aplicar “Método que Revela la Intención”. Voilà.

Objetos Servicio

Decoupling different concerns from chubby classes

4/4 Objetos Servicio

Si agregamos nueva funcionalidad a un objeto existente

• Se acopla a nuevas dependencias

• Pierde cohesión

• Los tests se tornan más complejos y lentos

• La descripción del objeto incluye “y"/"o" (SRP)

4/4 Objetos Servicio

Modelando nuevo comportamiento

4/4 Objetos Servicio

• Si fuera un concepto del dominio del problema es un nuevo Modelo (una Entidad).

• Si es sólo un proceso o algoritmo (sin estado),es un nuevo Servicio.

Términos del libro “Domain Driven Design”.

4/4 Objetos Servicio

Modelando nuevo comportamiento

Próximos Pasos

• Envíen Pull Requests en GitHub

• Es una mina de oro para enseñar y aprender

• Eligen proyectos y mentores como en un supermercado eligen galletitas

Próximos Pasos

Próximos Pasos: las 4 reglas

• Clases de a lo sumo 100 líneas

• Métodos de a lo sumo 5 líneas

• Métodos con 4 argumentos como máximo

• Un controlador instancia sólo una variable de instancia

Próximos Pasos

Porqué Refactorizar

No sólo sobre estética (aunque es consecuencia)

También conocimiento compartido,encontrar bugs, y performance.

Próximos Pasos

Porqué Refactorizar

Trabajamossobre las herramientas con que trabajamos.

Somos usuarios y creadores.

Próximos Pasos

Porqué Refactorizar

Si tengo un sesgo, elijo “over-engineering".

Porque “Under-engineering” es caro, riesgoso y superpoblado.

Próximos Pasos

¡Gracias!

Preguntas bienvenidas.

top related