refatoração

91
Desenvolvimento Baseado em Testes Refatoração

Upload: eduardo-mendes-de-oliveira

Post on 11-Jun-2015

907 views

Category:

Technology


1 download

DESCRIPTION

Definição de refatoração, quando utilizar, exemplos e técnicas

TRANSCRIPT

Page 1: Refatoração

Desenvolvimento Baseado em Testes

Refatoração

Page 2: Refatoração

Agenda

• Refatoração

• Exemplos

• Técnicas de Refatoração

���2

Page 3: Refatoração

Refatoração

Page 4: Refatoração

O que é?

• “É o processo de realizar mudanças em código existente e funcional sem alterar seu comportamento”

!

• Alterar COMO o código

• NÃO alterar O QUE ele faz

!

• Aprimorar a estrutura interna

Page 5: Refatoração

Qual a relação com TDD?

Page 6: Refatoração

Refatoração e TDD

• Após implementar o código mais simples para fazer o teste passar

• refatoramos o código para remover as duplicações que adicionamos para ver o teste passar

• Como temos um conjunto seguro de testes,

• então podemos refatorar com confiança

Page 7: Refatoração

O que nos motiva a refatorar?

Page 8: Refatoração

Motivação

• facilitar a adição de código novo

• melhorar o projeto existente

• obter um melhor entendimento de código

• tornar a programação menos irritante

Page 9: Refatoração

Quando refatorar?

Page 10: Refatoração

Contextos

• quando existe duplicação de código

• quando a intenção é obscura

• percebemos que o código e/ou sua intenção não são claros

• ex: lógica condicional complicada

• quando detectamos problemas de código (“bad smells”)

• ou indícios de problemas

Page 11: Refatoração

Duplicação

• Falência de um bom código

• Existem várias formas

• simples e óbvios

• índícios

• disfarçados

Page 12: Refatoração

Say Everything Once and Only Once !

Don’t Repeat Yourself

Page 13: Refatoração

Diga tudo uma vez e apenas uma vez !

Não se repita

Page 14: Refatoração

Exemplos

Page 15: Refatoração

Duplicação: 1.º exemplo

Page 16: Refatoração

def save if (arquivo.nil?) return false end diretorio = Diretorio.new(arquivo) diretorio.add(arquivo) diretorio.close() return true end

def saveAs arquivo = view.file if (arquivo.nil?) return false end diretorio = Diretorio.new(arquivo) diretorio.add(arquivo) diretorio.close() return true end

Page 17: Refatoração

def save if (arquivo.nil?) { return false } diretorio = Diretorio.new(arquivo) diretorio.add(arquivo) diretorio.close() return true end

def saveAs arquivo = view.file save end

Page 18: Refatoração

Duplicação: 2.º exemplo

Page 19: Refatoração

class MovieList def initialize @movies = [] @number_of_movies = 0 end def size @movies.size end def add movie_to_add @movies << movie_to_add @number_of_movies = @number_of_movies + 1 endend

Page 20: Refatoração

class MovieList ! def initialize @movies = [] @number_of_movies = 0 end! def size @movies.size end def add movie_to_add @movies << movie_to_add end!end

Page 21: Refatoração

Intenção obscura

Page 22: Refatoração

O que torna um código claro?

• Escolher bons nomes

• dicionário na mão para ajudar a comunicar nossa intenção

• TDD

• Como escrevemos 1º o teste, somos forçados a pensar na interface do código antes da sua implementação

• oportunidade de pensar a partir do ponto de vista do usuário da classe

Page 23: Refatoração

Problemas de código Code Smells

Page 24: Refatoração

Bad Smells• Excesso de comentários

• Classes de dados

• Código duplicado

• Intimidade inapropriada

!

!

!

• Classes muito grandes

• Classes “preguiçosas”

• Métodos longos

• Switches

Page 25: Refatoração

def init // set the layout content_pane.layout(FlowLayout.new) ! // create the list movie_list = List.new(my_editor.movies) scroller = ScrollPane.new(movie_list) content_pane.add(scroller) ! // create the field movie_field = TextField.new(16) content_pane.add(movie_field) ! // create theadd button add_button = Button.new(“Add") .... end

Excesso de comentários

Page 26: Refatoração

Classes de dadosclass Ponto attr_accessor :x, :y def initialize(x = 0, y = 0) @x = x; @y = y; end end

Page 27: Refatoração

Com intimidade

def temperatura t = estacao.termometro t.temperatura end

Page 28: Refatoração

Sem intimidade

def temperatura estacao.temperatura end

Page 29: Refatoração

Classes muito grandes

• Desproporcional às outras

• Por quê?

• tenta fazer muita coisa?

• possui muito código condicional?

• possui muito comportamento condicional?

• Como identificar?

Page 30: Refatoração

Classes “preguiçosas”

• Classes tão pequentas que não justificam sua existência

• Devem ser fundidas à outras classes

Page 31: Refatoração

Switchesclass Empregado // 0 - engenheiro, 1 - vendedor, 2 - gerente attr_accessor :tipo_empregado ! def nome_do_departamento case @tipo_empregado when 0 return "Engenheiro" when 1 return "Vendedor" when 2 return "Gerente" else return "Desconhecido" end end

end

Page 32: Refatoração

Dica

• Princípios de Orientação a objetos

• Design Patterns

Page 33: Refatoração

Como refatorar?

Page 34: Refatoração

Como refatorar

1. Estrutura de testes que proporcionem feedback

2. Pequenos passos

3. IDEs

Page 35: Refatoração

Técnicas de refatoração

Page 36: Refatoração

Refatorações• Extrair classe

• Extrair interface

• Extrair método

• Substituir código digitado por subclasses ou objeto de valor

• Substituir condicional por polimorfismo

• Utilizar métodos gabaritos

• Utilizar variavel explicativa

• Substituir construtores por métodos fábrica

• Substituir herança por delegação

• Substituir números mágicos por constantes

Page 37: Refatoração

Extrair Classe

• Contexto

• classes muito grandes

• comportamento disperso

• Solução

• fracionar as classes em pedaços menores mais coesos

• Extração de comportamentos para uma nova classe

Page 38: Refatoração

class MovieListWriter attr_accessor :destination! def initialize(aWriter = nil) destination = aWriter; end! def write_movie_list(a_list) a_list.movies.each do |movie| write_movie(movie) end end! def write_movie(a_movie) destination.write(a_movie.name) destination.write('|') destination.write(a_movie.category.to_s) destination.write('|') begin destination.write(a_movie.rating.to_s) rescue UnratedException => ex destination.write("-1") end destination.write('\n') endend

Page 39: Refatoração

class Movie // ... def write_to(destination) destination.write(name) destination.write('|') destination.write(category.to_s) destination.write('|') begin destination.write(rating.to_s) rescue UnratedException => ex destination.write("-1") end destination.write('\n'); end // ...end

Page 40: Refatoração

class MovieList write_to(destination) movies.each do |movie| movie.write_to(destination) end end end

Page 41: Refatoração

Extrair Interface

• Contexto

• Se quer abstrair a forma de uma implementação concreta

• Comportamentos importantes substituíveis ou reversíveis

• Solução

• criar interfaces para poder substituir o concreto tardiamente

Page 42: Refatoração

public class MovieList { private Collection<Movie> movies = new ArrayList<Movie>();!

public int size() { return movies.size(); } public void add(Movie movieToAdd) { movies.add(movieToAdd); }}

Page 43: Refatoração

public interface IMovieList {!

public abstract int size();!

public abstract void add(Movie movie);!

}

Page 44: Refatoração

public class MovieList implements IMovieList { //...}

Page 45: Refatoração

Strategy

Page 46: Refatoração

Extrair Método

• Contexto

• métodos muito longos

• lógicas de complexo entendimento

• Solução

• fracionar o método em métodos menores mais coesos

• Extração de comportamentos para novos métodos

Page 47: Refatoração

public void init() { getContentPane().setLayout(new FlowLayout()); movieList = new JList(myEditor.getMovies()); JScrollPane scroller = new JScrollPane(movieList); getContentPane().add(scroller); movieField = new JTextField(16); getContentPane().add(movieField); addButton = new JButton("Add"); ....

}

Page 48: Refatoração

public void init() { // set the layout getContentPane().setLayout(new FlowLayout()); ! // create the list movieList = new JList(myEditor.getMovies()); JScrollPane scroller = new JScrollPane(movieList); getContentPane().add(scroller); ! // create the field movieField = new JTextField(16); getContentPane().add(movieField); ! // create theadd button addButton = new JButton("Add"); .... }

Page 49: Refatoração

public void init() { setLayout(); initMovieList(); initMovieField(); initAddButton(); } private void setLayout() { getContentPane().setLayout(new FlowLayout()); } private void initMovieList() { movieList = new JList(getMovies()); JScrollPane scroller = new JScrollPane(movieList); getContentPane().add(scroller); } private void initMovieField() { movieField = new JTextField(16); getContentPane().add(movieField); } private void initAddButton() {...

Page 50: Refatoração

• Se um trecho de código duplicado diferentes do programa

Extrair Método 2

Classe1

Page 51: Refatoração

Extrair Método 2• Se as duplicatas de código devem permanecer

sempre iguais, ou seja, uma vez que se realize uma alteração em uma delas, as demais devem refletir a alteração

Classe1

Page 52: Refatoração

public void doGet(HttpservletRequest request, HttpServletResponse response) throws ServletException, IOException{ String p = request.getParameter(“personagem”); request.setAttribute(“personagem”, p); //Mais código } !public void doPost(HttpservletRequest request, HttpServletResponse response) throws ServletException, IOException{ String p = request.getParameter(“personagem”); request.setAttribute(“personagem”, p); //Mais código }

Page 53: Refatoração

Extraindo o método• Concentre o código que se repete em um único

lugar, por exemplo, em um método e leve as dependências para lá

public void doGet(HtttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{

String p = request.getParameter(“personagem”); request.setAttribute(“personagem”, p); //Mais código }

public void novoMetodo(HtttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{

String p = request.getParameter(“personagem”); request.setAttribute(“personagem”, p); //Mais código }

Page 54: Refatoração

public void novoMetodo(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException{ ..... } public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException{ novoMetodo(request, response); } !

public void doPost(HttpservletRequest request, HttpServletResponse response)

throws ServletException, IOException{ novoMetodo(request, response); }

Page 55: Refatoração

Refatorações• Extrair classe

• Extrair interface

• Extrair método

• Substituir código digitado por subclasses ou objetos de valor

• Substituir condicional por polimorfismo

• Utilizar métodos gabaritos

• Utilizar variavel explicativa

• Substituir construtores por métodos fábrica

• Substituir herança por delegação

• Substituir números mágicos por constantes

Page 56: Refatoração

Substituir código digitado por subclasses • Contexto

• classes indicam subtipos através de código digitado

• Solução

• Criar uma subclasse para cada alternativa

• Vantagem

• Evitam-se complexos condicionais

Page 57: Refatoração

class Empregado //0 - engenheiro, 1 - vendedor, 2 - gerente attr_accessor :tipo_do_empregado //..end

Page 58: Refatoração

class Empregado // ...end!class Engenheiro < Empregado // ...end!class Vendedor < Empregado // ...end!class Gerente < Empregado // ...end

Page 59: Refatoração
Page 60: Refatoração

Substituir condicional por polimorfismo

• Contexto

• classes indicam subtipos através de código digitado

• Solução

• Criar uma subclasse para cada alternativa

• Vantagem

• Evitam-se complexos condicionais

Page 61: Refatoração

public class Empregado // 0 - engenheiro, 1 - vendedor, 2 - gerente attr_accessor :tipo_do_empregado! def nome_do_departamento case @tipoDoEmpregado when 0 return "Engenharia" when 1 return "Vendas" when 2 return "Gerência" else return "Desconhecido" end endend

Page 62: Refatoração

class Empregado def nome_do_departamento endend!class Engenheiro < Empregado def nome_do_departamento "Engenharia" endend!class Vendedor < Empregado def nome_do_departamento "Vendas" endend!class Gerente extends Empregado def nome_do_departamento "Gerência" endend

Page 63: Refatoração

Utilizar métodos gabaritos

• Contexto

• métodos em subclasses executam passos similares na mesma ordem

• os passos são diferentes

• Solução

• extraia os passos para métodos com mesma assinatura

• crie um método gabarito final na superclasse

• especialize os métodos nas subclasses

Page 64: Refatoração

Contextopublic class Cafe { public void prepararReceita(){ ferverAgua(); misturarCafeComAgua(); servirNaXicara(); adicionarAcucarELeite(); }

!

!

!!!

public class Cha { ! public void prepararReceita(){ ferverAgua(); misturarChaComAgua(); servirNaXicara(); adicionarLimao(); }

Page 65: Refatoração
Page 66: Refatoração

Utilizar variáveis explicativas

• Contexto

• expressões complexas de se entender

• Solução

• extrair partes delas

• guardar resultados intermediários em variáveis bem nomeadas

• Vantagem

• código de melhor entendimento

Page 67: Refatoração

def calcular_total subtotal.mais(subtotal_taxavel.vezes(0.15))) .menos((subtotal().to_f > 100.0) ? (subtotal().vezes(0.10)) : 0)end

Page 68: Refatoração

def calcular_total taxa = subtotal_taxavel().vezes(0.15) total = subtotal.mais(taxa) qualificado_ao_desconto = subtotal.to_f > 100.0 desconto = qualificadoAoDesconto ? subtotal.vezes(0.10) : Dinheiro.new(0.0) total.menos(desconto)}

Page 69: Refatoração

Substituir construtor por métodos fábrica

• Contexto

• existência de diversos construtores para criar versões diferentes dos objetos

• pode haver confusão por falta de clareza de intenção do construtor

• Solução

• criar métodos fábrica estáticos

• Vantagem

• código de melhor entendimento

Page 70: Refatoração
Page 71: Refatoração

Como fazer• Execute um Extrair Método para isolar a lógica do

comportamento

• o método deve ser de classe

• repasse as dependências

• Teste

• Se o método fábrica não estiver no objeto desejado, utilize o Mover Método

• Teste

• Remova o construtor original se não há chamadas a ele

Page 72: Refatoração

Extrair Método

Page 73: Refatoração
Page 74: Refatoração
Page 75: Refatoração

Mover Método

Page 76: Refatoração
Page 77: Refatoração

Outra maneira Java

Page 78: Refatoração

public class Avaliacao { private int valor = 0; private String revisor = null; private String revista = null;! public Avaliacao(int umaAvaliacao) { this(umaAvaliacao, "Anonimo", ""); }! public Avaliacao(int umaAvaliacao, String umRevisor) { this(umaAvaliacao, umRevisor, ""); }! public Avaliacao( int umaAvaliacao, String umRevisor, String umaRevista) { valor = umaAvaliacao; revisor = umRevisor; revista = umaRevista; } // ...}

Page 79: Refatoração

public static Avaliacao novaAvaliacaoAnonima(int valor) { return new Avaliacao(valor, "Anonimo", "");}!public static Avaliacao novaAvaliacao(int valor, String revisor) { return new Avaliacao(valor, revisor, "");}!public static Avaliacao novaCritica( int valor, String revisor, String revista) { return new Avaliacao(valor, revisor, revista);}!private Avaliacao( int umaAvaliacao, String umRevisor, String umaRevista) { valor = umaAvaliacao; revisor = umRevisor; revista = umaRevista;}

Page 80: Refatoração

starWars.adicionarAvaliacao(new Avaliacao(2));starWars.adicionarAvaliacao(new Avaliacao(4, "Joel Barbosa"));starWars.adicionarAvaliacao( new Avaliacao(5, "PH Santos", "TechTudo"));

Page 81: Refatoração

starWars.adicionarAvaliacao( Avaliacao.novaAvaliacaoAnonima(2));!starWars.adicionarAvaliacao( Avaliacao.novaAvaliacao(4, "Joel Barbosa"));!starWars.adicionarAvaliacao( Avaliacao.novaCritica(5, "PH Santos", "TechTudo"));

Page 82: Refatoração

Substituir herança por delegação

• Contexto

• uma subclasse utiliza apenas uma parte da interface de sua superclasse e não reutiliza os dados

• Solução

• crie um campo do tipo da superclasse

• refatore os métodos que utilizam o comportamento da superclasse

• remova a herança

Page 83: Refatoração
Page 84: Refatoração
Page 85: Refatoração

Substituir números mágicos por constantes ou enums

• Contexto

• valores literais no código que possuem um significado

• Solução

• crie uma constante e nomeie-a com o seu significado

• substitua os valores literais pelas constantes

• Vantagem

• código de melhor entendimento

Page 86: Refatoração

def energia_potencial(massa, altura) massa * altura * 9.81end

Page 87: Refatoração

CONSTANTE_GRAVITACIONAL = 9.81 def energia_potencial(massa, altura) massa * altura * CONSTANTE_GRAVITACIONAL end

Page 88: Refatoração

Resumo

• Um pouco de refatoração

• o que é

• técnicas específicas

• alguns indicadores

• Existe problema?

• Deve-se refatorar em pequenos incrementos

Page 89: Refatoração

Bibliografia

• ASTELS, David. Test-Driven Development: A Pratical Guide. Prentice Hall, 2003.

• FOWLER, Martin; BECK, Kent; BRANT, John; Opdyke, William; ROBERTS, Don. Refactoring: Improving The Design of Existing Code.

• KERIEVSKY, Joshua. Refatoração Para Padrões. Porto Alegre: Bookman, 2008.

Page 90: Refatoração
Page 91: Refatoração

Bibliografia

• ASTELS, David. Test-Driven Development: A Pratical Guide. Prentice Hall, 2003.

• FOWLER, Martin; BECK, Kent; BRANT, John; Opdyke, William; ROBERTS, Don. Refactoring: Improving The Design of Existing Code.

• KERIEVSKY, Joshua. Refatoração Para Padrões. Porto Alegre: Bookman, 2008.