Continuando com a seqüência de posts com títulos polêmicos que comecei dizendo que “Comentário no código é para os fracos“, segue um curso básico de refactoring para quem é pobre (por que vou utilizar o eclipse que é uma excelente IDE e alem de tudo é “di grátis”), e preguiçoso, por que o eclipse vai fazer quase todo o trabalho para nós.
O ponto de partida vai ser o “exercício” que deixei no final do post sobre comentários no código.
Par quem não quiser ler todo o outro post, o código inicial vai ser este abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | package blog; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class VeryBadlyNamedFile { private static final char[] asdfg = new char[] {'I', ' ', 'c', 'a', 'n', ' ', 'd', 'o', ' ', 'v', 'e', 'r', 'y', ' ', 'u', 'g', 'l', 'y', ' ', 'c', 'o', 'd', 'e'}; private String an; private BufferedReader rfsdw; private FileReader temp; public VeryBadlyNamedFile(String an, BufferedReader rfsdw, FileReader temp) { super(); this.an = an; this.rfsdw = rfsdw; this.temp = temp; } public void doIt() throws IOException { ctfiidne(); startDoing(); try { canIDoAnyThing(); } catch (RuntimeException yicdet) { nowReallyDoIt(); } } private void nowReallyDoIt() { firstDoTheOtherThing(); reallyDoItInternal(); } private void firstDoTheOtherThing() { rfsdw = new BufferedReader(temp); } private void reallyDoItInternal() { while (true) { try { imDoingIt(); } catch (Exception e) { break; } } } private void imDoingIt() throws Exception { String s = rfsdw.readLine(); if (s == null) throw new Exception("hahaha, I bet you did not understood the code"); System.out.println(s); } private void ctfiidne() throws IOException { File a = new File(an); if (!a.exists()) { FileWriter wrfedsd = new FileWriter(a); wrfedsd.write(asdfg); wrfedsd.close(); } } private void canIDoAnyThing() { if (new File(an).exists() && new File(an).canRead() && new File(an).canWrite()) throw new RuntimeException(); } private void startDoing() throws FileNotFoundException { File f = new File(an); temp = new FileReader(f); } } |
Antes de começar o refactoring, vamos definir o que é refactoring de uma forma bem simples:
Refactoring = Alterar partes do código de uma aplicação sem quebrar outras partes da aplicação que dependam daquele código.
Refactoring é uma forma de melhorar o design de um código existente enquanto ele continua funcionando.
Gosto muito de uma frase espetacular do Fowler, um dos papas do desenvolvimento ágil, que coloco abaixo:
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
-Martin Fowler et al, Refactoring: Improving the Design of Existing Code, 1999
Nos vamos utilizar os recursos de refactoring do Eclipse para transformar este lixo acima em alguma coisa legível, mantendo exatamente o mesmo comportamento, ou seja, sem quebrar o código que já funciona.
Para facilitar o trabalho, este tutorial (se é que pode ser chamado de tutorial), vai ser um simples “passo a passo” que eu utilizei para alterar este código no Eclipse, que é a minha segunda IDE preferida (a melhor de todas na minha opinião é o IntelliJ IDEA, mas se eu utilizasse este, não seria um tutorial para quem é pobre
)
Siga os passos abaixo:
- Primeiro vamos renomear a classe, com o arquivo aberto, o cursos sobre o nome da classe (VeryBaclyNamedFile) pressione ALT+SHIFT+R e substitua o nome da classe por “TextFileToScreenPrinter” depois pressione “Enter”
- Agora esta na hora de alterar os nomes de alguns métodos. Repetindo exatamente o mesmo procedimento (ALT+SHIFT+R, altera nome, Enter), faça as seguintes alterações nos nomes de métodos e variáveis:
- asdfg -> standardContentForNewFiles
- an -> fileName (neste caso no parâmetro do construtor e na variável de instância)
- ctfiidne -> ensureFileAlreadyExists
- canIDoAnyThing -> abortIfCanNotReadOrWriteFile
- startDoing -> createFileReader
- temp -> rawFileReader (neste caso no parâmetro do construtor e na variável de instância)
- rfsdw -> fileLineReader (neste caso no parâmetro do construtor e na variável de instância)
- nowReallyDoIt -> printEachLineFromFileToConsole
- yicdet -> e
- firstDoTheOtherThing -> createLineReaderFromFileReader
- reallyDoItInternal -> forEachLineInTheFilePrintItOnTheScreen
- imDoingIt -> printNextLineOfFileToStdOutButFailIfThereAreNoMoreLines
- Agora com nomes menos ruins para os métodos vamos começar a organizar um pouco as coisas, dentro do método “abortIfCanNotReadOrWriteFile” selecione o trecho “new File(fileName).exists()”, pressione as teclas ALT+SHIFT+L e de o nome “fileExists” para a variável, agora repita a operação para o bloco “new File(fileName).canRead()” e de o nome “canReadTheFile” para a variável, e por último repita a operação com o bloco “new File(fileName).canWrite()” e nomeie a variável “canWriteToTheFile”.
- Agora podemos ler facilmente que a lógica deste método esta invertida, então precisamos negar as variáveis no “if” dentro do método “abortIfCanNotReadOrWriteFile”.
- Agora dentro do método “doIt”, podemos remover o try/catch.
- Editando o método “createLineReaderFromFileReader”, vamos mudar o tipo sendo utilizado para inicializar a variável “fileLineReader” para um java.util.Scanner, para isto basta substituir o bloco “new BufferedReader(rawFileReader);” por “new Scanner(rawFileReader);”. No momento em que isto for feito, o eclipse vai reclamar que não sabe o que é “Scanner”, então precione CTRL+1 com o cursor sobre a palavra “Scanner” e selecione “import java.util.Scanner”. Agora a reclamação é um “type mismatch”, simplesmente precione CTRL+F1 novamente e selecione a opção “Change the type of fileLineReader to Scanner”. E por último, vamos adicionar a seguinte linha neste método:
1
fileLineReader.useDelimiter("n");
- Para melhorar um pouco o código, vamos remover os dois parâmetros extras do construtor da clase, o único parâmetro que deve ficar no construtor é o primeiro, que aponta para o arquivo que deve ser lido. Para fazer isto, coloque o cursor sobre o construtor e pressione ALT+SHIFT+C, remova os dois parâmetros extras no dialogo e clique em finish, como esta alteração vai causar um erro no código, será necessário clicar em continue no próximo dialogo. Para corrigir o erro, remova as linhas 19 e 21, o eclipse deve ter sublinhado estas linhas em amarelo.
- O código esta começando a parecer um pouco melhor, mas ainda precisa de alterações (no momento ele não deve estar nem compilando se você seguiu todos os passos até agora). Vamos então remover o método “printNextLineOfFileToStdOutButFailIfThereAreNoMoreLines”, e alterar o código do método “forEachLineInTheFilePrintItOnTheScreen” para algo parecido com o bloco abaixo:
1 2 3 4 5 6 7 8 9
Iterable<string> lineIterator = new Iterable<string>(){ @Override public Iterator<string> iterator() { return fileLineReader; } }; for(String line : lineIterator){ System.out.println(line); }
- Para melhorar o código do método, selecione a criação do iterator toda (deve ser da linha 40 a 45 no arquivo), pressione as teclas ALT+SHIFT+M e digite o nome “initializeLineIteratorFromLineReader” para o novo método.
- Agora utilizando o refactoring de rename (ALT+SHIFT+R) altere o nome da variável “lineIterator” para “linesInTheFile”, desta forma o último bloco do método pode ser lido exatamente igual ao nome do método.
- Agora no método “ensureFileAlreadyExists” vamos alterar o conteúdo para o seguinte bloco:
1 2 3 4 5 6 7
File file = new File(fileName); boolean fileExists = file.exists(); if (!fileExists) { FileWriter fileWriter = new FileWriter(file); fileWriter.write(conteudoPadraoParaNovoArquivo); fileWriter.close(); }
- Se você já esta se acostumando com os refactorings, vai perceber que o que foi feito foi um “extract variable” na condição do IF, e dois renames, um na variável que aponta para o arquivo e outro na variável que aponta para o FileWriter.
- Agora que quase todas as variáveis e métodos tem nomes decentes, podemos fazer algumas alterações na lógica, para limpar um pouco o código desta classe, como por exemplo, remover o método “createFileReader” e a variável “rawFileReader”, alterar o método “createLineReaderFromFileReader” para passar “new File(fileName)” como parâmetro na criação do Scanner.
- Isto vai gerar um erro de compilação, com o CTRL+1 poderemos adicionar a exception que falta no throws do método e tudo vai ficar OK.
- Podemos renomear o método “createLineReaderFromFileReader” para apenas “createLineReader”.
Pronto, o Eclipse acabou de nos ajudar a ter um código menos porco na classe do post sobre comentários de código.
Claro que o código ainda não esta nenhum primor, mas a idéia deste post era mostrar que é possível utilizar recursos da IDE para facilitar o refactoring de código porco quando este for encontrado.
E não se iluda, se você estuda para melhorar o seu conhecimento sobre desenvolvimento e ser um profissional cada vez melhor, provavelmente o código que você escreveu a dois meses atrás você ache muito ruim hoje.
Só para finalizar o post, o seu código deve ter ficado parecido com este:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | package blog; import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.util.Iterator; import java.util.Scanner; public class TextFileToScreenPrinter { private static final char[] standardContentForNewFiles = new char[] {'I', ' ', 'c', 'a', 'n', ' ', 'd', 'o', ' ', 'v', 'e', 'r', 'y', ' ', 'u', 'g', 'l', 'y', ' ', 'c', 'o', 'd', 'e'}; private String fileName; private Scanner fileLineReader; public TextFileToScreenPrinter(String fileName) { super(); this.fileName = fileName; } public void doIt() throws IOException { ensureFileAlreadyExists(); abortIfCanNotReadOrWriteFile(); printEachLineFromFileToConsole(); } private void printEachLineFromFileToConsole() throws FileNotFoundException { createLineReader(); forEachLineInTheFilePrintItOnTheScreen(); } private void createLineReader() throws FileNotFoundException { fileLineReader = new Scanner(new File(fileName)); fileLineReader.useDelimiter("n"); } private void forEachLineInTheFilePrintItOnTheScreen() { Iterable<string> linesInTheFile = initializeLineIteratorFromLineReader(); for(String line : linesInTheFile){ System.out.println(line); } } private Iterable<string> initializeLineIteratorFromLineReader() { Iterable<string> lineIterator = new Iterable<string>(){ @Override public Iterator<string> iterator() { return fileLineReader; } }; return lineIterator; } private void ensureFileAlreadyExists() throws IOException { File file = new File(fileName); boolean fileExists = file.exists(); if (!fileExists) { FileWriter fileWriter = new FileWriter(file); fileWriter.write(standardContentForNewFiles); fileWriter.close(); } } private void abortIfCanNotReadOrWriteFile() { boolean fileExists = new File(fileName).exists(); boolean canReadTheFile = new File(fileName).canRead(); boolean canWriteToTheFile = new File(fileName).canWrite(); if (!fileExists && !canReadTheFile && !canWriteToTheFile) throw new RuntimeException(); } } |
Atalhos do Eclipse Ganimede que todo preguiçoso inteligente deveria decorar
- ALT+SHIFT+M -> Extract method
- ALT+SHIFT+L -> Extract Local Variable
- ALT+SHIFT+R -> Rename
- CTRL+1 -> Quick Fix
- ALT+SHIFT+X,J -> Executa aplicação Java
- ALT+SHIFT+X,T -> Executa Unit Test
- ALT+SHIFT+X -> Executar, se o tipo não for especificado, um menu será aberto em 5 segundos no canto inferior direito
- CTRL+SPACE -> Code completion
- CTRL+SHIFT+SPACE -> Parameter completion
- CTRL+3 -> Atalho super mega mágico para qualquer tarefa dentro do eclipse,digite o nome do que quer fazer no dialogo que vai aparecer
- ALT+SHIFT+S -> menu “source” onde ficam comandos como “Generate getters and setters” e “Override/Implement methods”
- ALT+SHIFT+T -> Menu de refactoring
Atalhos do Eclipse apra usuáriso de MAC
- COMMAND+OPTION+M -> Extract method
- COMMAND+OPTION+L -> Extract Local Variable
- COMMAND+OPTION+R -> Rename
- COMMAND+1 -> Quick Fix
- COMMAND+OPTION+X,J -> Executa aplicação Java
- COMMAND+OPTION,T -> Executa Unit Test
- COMMAND+OPTION+X -> Executar, se o tipo não for especificado, um menu será aberto em 5 segundos no canto inferior direito
- CTRL+SPACE -> Code completion
- COMMAND+3 -> Atalho super mega mágico para qualquer tarefa dentro do eclipse,digite o nome do que quer fazer no dialogo que vai aparecer
- COMMAND+OPTION+S -> menu “source” onde ficam comandos como “Generate getters and setters” e “Override/Implement methods”
- COMMAND+OPTION+T -> Menu de refactoring
E o atalho de teclado mais mágico de todos é:
COMMAND+SHIFT+L (CTRL+SHIFT+L em PCs) -> Lista os atalhos de teclado.
Acho que isto já esta bom para começar, se você for realmente um preguiçoso inteligente (o tipo que passa um pouco mais de tempo pensando para ter uma solução melhor agora e trabalhar menos no futuro), provavelmente você vai prestar atenção nos menus do eclipse e vai ir decorando as teclas de atalho com o tempo
PS.: Será que alguém vai ficar ofendido com o titulo deste post e vai ficar reclamando que não é pobre ou não é preguiçoso?

Muito bom!
Isso pode economizar horas….
Abraço,
André Faria
Excelente post..
Tendo como base o título do post, vi que também sou um Pobre e Preguiçoso.
Att
Excelente post..
Tendo como base o título do post, vi que também sou um Pobre e Preguiçoso.
Nelson.
Att
Muito bom mesmo, adoro refatorar, apesar de as vezes (só as vezes akaukaua,) ser um preguiçoso pobre.
Acho que o pessoal de banco de dados deveria fazer esse curso para não criar
nomes de tabelas como:
“comsolicitmovmat”, “r034fun”, “dupnumseq”,
nem colunas com nomes como:
“redprodutkey”, “dergrpmatprima” hehehe
Eu sou preguiçoso e não sou pobre, afinal trabalho com um Mac (:P), então aí vão os atalhos de teclado do Eclipse no Mac OSX:
* COMMAND+OPTION+M -> Extract method
* COMMAND+OPTION+L -> Extract Local Variable
* COMMAND+OPTION+R -> Rename
* COMMAND+1 -> Quick Fix
* COMMAND+OPTION+X,J -> Executa aplicação Java
* COMMAND+OPTION,T -> Executa Unit Test
* COMMAND+OPTION+X -> Executar, se o tipo não for especificado, um menu será aberto em 5 segundos no canto inferior direito
* CTRL+SPACE -> Code completion
* Não achei o que faz parameter completion
* COMMAND+3 -> Atalho super mega mágico para qualquer tarefa dentro do eclipse,digite o nome do que quer fazer no dialogo que vai aparecer
* COMMAND+OPTION+S -> menu “source” onde ficam comandos como “Generate getters and setters” e “Override/Implement methods”
* COMMAND+OPTION+T -> Menu de refactoring
E o atalho de teclado mais mágico de todos é:
* COMMAND+SHIFT+L (CTRL+SHIFT+L em PCs) -> Lista os atalhos de teclado.
Wilerson, obrigado pela contribuição, estou incluindo estes atalhos no post
[...] Urubatan escreveu um artigo muito interessante sobre Refactoring e outro sobre “Comentário no código é para os [...]
Pude perceber que realizar o refactoring e testes no codigo leva muito do tempo de programação, porém deixa mais facil para reutilizar o codigo em qualquer aplicação similar, que apresente a mesma situação.
Obrigado pelo post.
Urubantan,
Parabéns p/ Post/WorkShop, tanto p/ quem newbie em programação, com a gente velho de gerra!!
)
Muito bom NÃO ter traduzido o termo ‘Refactoring’.
Só 1 ressalva: no últ. método (abortIfCanNotReadOrWriteFile() ) vc destacou somente “clareza de código”. P/ melhorar poderia ter isntanciado File apena 1 vez e referenciar seu (único) identificador. = Performance (na verdade: + economia de memória).