Blog do Urubatan
msgbartop
Desenvolvedor, Palestrante, Escritor, Nerd Assumido e Pai do Marcus :D
msgbarbottom

30 Sep 09 Seja comunicativo, diga por que escreveu este código

Bom, no último post eu disse para você não ser repetitivo, e não escrever nos comentários o que o seu código faz. Se você precisa comentar o que o seu código faz, você realmente precisa refatorar o seu código e torna-lo mais legível
Mas comentários no código não são sempre ruins, o ruim é você escrever em português o que o seu código faz em java, mas existem diversas situações que veremos a seguir onde os comentários são ótimos, ou pelo menso são úteis …

Por exemplo, na interface pública de um serviço, um JavaDoc informando para que serve aquele serviço e com exemplos de utilização são sempre muito bem vindos, principalmente se este comentário informar quais são as validações que serão feitas nos parâmetros e quais são as restrições ao uso daquele serviço …
Eu não posso pegar exemplos de projetos em que estou trabalhando por que isto seria motivo para demissão por justa causa por quebra de sigilo :D
Então vamos ficar com alguns exemplos públicos e alguns exemplos mais simples que eu conseguir pensar na hora …

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * This is the only interface you need to create and manage system users, but if you need to manage user privileges take a look at the @link{PrivilegesService} interface
 */
public interface UsersService {
   /**
    * This method should be used only if you are already authenticated in the system, do no forget to send the session id header when calling methods that are not public like this one.
    * To create a user you need to provide all the needed information, this method will create  user in the database and then will try to create the user in the backend system, if the creation works the two accouns will be linked otherwise the database reccord will be erased. 
    * @param userName the user name must be unique within the LDAP domain, this means that there can
    * @param password
    * @param email
    */ 
   public User createUser(String userName, String password, String email);
}

Neste exemplo, não há necessidade alguma de colocar um comentário dizendo que o método createUser cria um usuário, mas o javadoc do exemplo informa de algumas pré condições para a execução do método e também diz qual o fluxo de execução e possíveis falhas. Como neste exemplo o UsersService é a interface publica de um serviço, esta documentação é bastante bem vinda, pois vai ajudar os usuários que vão acessar o serviço a saber o que fazer antes de criar um usuário e a saber possíveis motivos para as falhas.

Bom, acho que todos os outros exemplos que eu procurar vão ser parecidos com isto, resumindo este post e o anterior:
Se um comentário diz o que o método faz, o código é um lixo ou o comentário é redundante, o que o método faz tem que ser óbvio pelo nome do método
Se o comentário diz quando o método deve ser utilizado, algumas pré condições, o fluxo de execução, possíveis erros, e principalmente se ele esta em uma interface publica do sistema, que vai ser entregue a outra equipe/programador/empresa. o comentário é útil
Se o comentário esta em algum método ou variável privada, provavelmente tem algum problema por ai.

Acho que com isto eu encerro este assunto de comentários, eu posso ter me expressado errado, ou o mais provável, muitas pessoas leram o título do post e saíram berrando antes de ler o resto, mas a idéia básica é, o seu código tem que ser muito fácil de ler, os nomes das classes, métodos, variáveis, pacotes e quaisquer arquivos envolvidos no sistema devem dizer o que são, para que servem e por que estão ali. Se você esta colocando um comentário que diz que o método “create” cria um usuário, você deveria chamar o método de “createUser”, mas se o comentário esta informando o usuário do método de que o nome de usuário utilizado não precisa ser único mas o email sim, pois é este o campo utilizado como chave, ai você tem um comentário útil (e um sistema extranho :D ).

Não é por que o seu código tem comentários que ele é um lixo, mas tome cuidado por que é muito fácil utilizar comentários como desculpa para código ruim. E neste caso, talvez você precise estudar um pouco de rafactoring.

Tags: , , , ,

11 May 09 Em time que esta ganhando se mexe sim (Refactoring básico)

Por mais que você seja um excelente programador, que todo o seu código funcione perfeitamente na primeira vez em que é executado (o que eu acho bem pouco provável que aconteça), por mais que você conheça pouco do código do sistema, ou por qualquer outro motivo que você possa lembrar agora ou daqui a 10 anos.
Em código que esta funcionando se mexe sim!
Mas por que estou dizendo isto? Porque se você fizer como eu, e de vez em quando, mas só de vez em quando para não ficar muito decepcionado consigo mesmo, pegar algum código que você escreveu no mês passado, ou a seis meses atrás, ou a um, dois, cinco ou dez anos, você vai achar este código muito mal escrito, mal organizado, feio, escrito por alguém que ainda precisava aprender tudo o que você aprendeu neste intervalo.
Se isto não acontecer com você, com certeza você está se tornando um programador medíocre que não aprendeu absolutamente nada neste intervalo, que não está melhor hoje do que era na semana passada, ou no mês passado ou no ano passado.
Esta sensação de que o código velho é ruim, não quer dizer que você era um programador ruim, é apenas o sinal de que você se esforçou e que hoje você é muito melhor do que era quando escreveu aquele código.

Ok, e o que isto tem a ver com este post? Tudo!
Se este código velho faz parte de algum sistema, biblioteca, projeto ou qualquer coisa do gênero que você não trabalha mais, deixe da forma como está, as pessoas que estão trabalhando nele agora que se preocupem com ele. Mas se ao contrário ele ainda faz parte de um código que você evolui dia a dia, então é sua responsabilidade fazer com que este código velho e maltrapilho, escrito por você ou não, evolua também, pelo menos o suficiente para não atrapalhar o código novo, escrito por este programador muito melhor do que aquele que escreveu o lixo que está sob seus olhos, mesmo que este tenha sido você ontem.
E este ato de piedade, generosidade e auto compaixão, é chamado de refactoring.
Ou seja, você vai fazer com que o código legado, melhore, seja mais testável, mais estável, mais bonito, sem quebrar todo o resto do sistema que já depende daquele pedaço de lixo que você escreveu no passado :D
E por que isto é também um ato de “auto compaixão”? Porque como eu ouvi um amigo comentar várias vezes, assim você esta diminuindo a quantidade de problemas legados que você vai ter que lidar no futuro próximo.

O que é rafactoring

Segundo a wikipedia: Refatoração (do inglês Refactoring) é o processo de modificar um sistema de software para melhorar a estrutura interna do código sem alterar seu comportamento externo.

Segundo o papa: Refatoração é uma técnica disciplinada para reestruturar um corpo de código existente, alterando a sua estrutura interna sem alterar o seu comportamento externo. O coração da técnica é uma série de pequenas transformações preservando o comportamento. Cada transformação (chamada de refactoring) faz um pouco, mas uma série de transformações podem produzir um resultado significante para a qualidade do sistema. Já que cada refatoração é pequena, é menos provável que ela cause algum problema. O sistema é mantido 100% funcional depois de cada refactoring. Reduzindo as chances de algum problema grave no sistema no final da reestruturação.

O que indica que um código precisa ser refatorado?

Basicamente se você acha que pode melhorar o código, isto já vale o rafactoring. Você achar que pode melhorar o código quer dizer que você esta sentindo que tem alguma coisa errada com ele, mesmo que você não tenha muita certeza de o que esta errado. Isto só quer dizer que você já aprendeu mais coisas depois que escreveu o código que esta lendo agora.
Mas podemos também formalizar isto um pouco, ou seja, definir alguns pontos nos quais todos concordam haver problemas no código, estes servem como argumento inclusive para você dizer que o código dos outros não esta muito bom, ou para você ter certeza de que o seu esta um lixo logo depois que escreve-lo.

  • Código duplicado (duplicated code)
  • Método longo (long method)
  • Classe grande (large class)
  • Lista de parâmetros longa (long parameter list)
  • Má indentação (Bad Indentation)

Os nomes em inglês estão ali por que eu não inventei isto, eu retirei esta pequena lista do livro do Fowler sobre rafactoring, também referenciado como “a biblia” pelo menos por mim :D

Alguns exemplos de refactoring

Ok, agora que você já sabe como identificar alguns problemas (é fácil, é só você achar que hoje sabe mais do que ontem :D ), vamos ver algumas soluções engarrafadas, prontinhas para beber, ou utilizar na sua IDE preferida (todas as IDEs Java hoje em dia possuem um suporte muito bom para refactorings).

  • Adicionar parâmetro – Add Parameter
     Muitas vezes é necessário adicionar mais um parâmetro em um método já existente, quando fizer isto, sera necessário alterar todos os lugares que já chamam o método existente, ou pelo menos deixar o método original como um delegate passando um valor padrão para o novo método, tudo depende de por que você esta utilizando este refactoring
  • Converter inicialização dinâmica por estática – Convert Dynamic to Static Construction
     Algumas vezes a utilização de reflection pode criar fraquezas para o sistema, se isto não for necessário, então prefira inicializar estaticamente os objetos
  • Converter inicialização estática por dinâmica – Convert Static to Dynamic Construction
     Algumas vezes o sistema tem bastante a ganhar com a utilização de reflection para reduzir código duplicado ou pelo menos para adicionar flexibilidade
  • Encapsular atributo – Encapsulate Field
     Nunca, e eu disse nunca mesmo, exponha um atributo diretamente em java, isto simplesmente não esta certo, prefica utilizar métodos de leitura e escrita
  • Extrair classe – Extract Class
     Várias vezes é possível encapsular parte do comportamento de uma classe maior em outra classe
  • Extrair interface – Extract Interface
     Algumas vezes acessar diretamente uma classe de outras partes do sistema aumenta muito o acoplamento do sistema, nestes casos, prefira criar uma interface com os métodos públicos e esconder um pouco a implementação, ou até mesmo criar mais de uma implementação
  • Extrair metodo – Extract Method
     Diversas vezes, quando o código de um método esta muito grande, é possível extrair parte dele para um outro método, preservando o comportamento e reduzindo a duplicação de código
  • Extrair sub classe – Extract Subclass
     Quando parte do comportamento de uma classe é utilizado apenas em situações especificas, criar uma sub classe pode ser uma boa idéia
  • Introduzir “objeto parâmetro” – Introduce Parameter Object
     Quando um método tem muitos parâmetros, algumas vezes é útil encapsular estes parâmetros em um objeto, e faer com que o método receba apenas aquele objeto como parâmetro
  • Mover classe – Move Class
     Quando um package tem muitas classes, algumas vezes é útil mover uma ou mais classes para outro package, também pode ser utilizado quando uma classe simplesmente esta no lugar errado.
  • Subir método – Pull Up Method
     Quando um método é implementado em mais de uma classe e estas classes compartilham uma super classe, pode ser útil mover a implementação deste método para esta super classe
  • Remover parâmetro – Remove Parameter
     Algumas vezes um dos parâmetros de um método simplesmente não é mais utilizado
  • Remover método de escrita – Remove Setting Method
     Quando um atributo deve ser somente leitura, não é interessante que exista um método de escrita para este atributo
  • Renomear método – Rename Method
     Algumas vezes o nome de um método simplesmente não parece certo, ou então não diz o que aquele método faz.


Claro que estes não são os únicos refactorings existentes, esta é apenas parte da lista que pode ser encontrada no site do livro de refactorings do Martin Fowler. Apenas as descrições foram escritas por mim, e mesmo no site, ou no livro do Fowler você não vai encontrar uma lista completa, por que é bem possível que outro refactoring seja criado hoje ou amanha, o importante é entender a idéia.

E eu tenho que decorar tudo isto? Tenho que fazer na mão e garantir que funciona?

Uma das coisas boas de ótimas idéias é que muita gente gosta delas, e acaba copiando.
Em algum momento do passado remoto do desenvolvimento java, quando as boas IDEs eram todas pagas, o pessoal da JetBrains, leu “a biblia” e disse: Que o IntelliJ IDEA ajude os desenvolvedores a fazerem os rafactorings para que o código se torne bonito e legível. E assim fez o IntelliJ IDEA.
Algum tempo depois, o eclipse copiou a idéia e passou a suportar muitos refactorings também, e hoje em dia o NetBeans também suporta muitos refactorings, e assim o desenvolvedor vive feliz podendo ser produtivo com a sua IDE favorita.
Eu adoro o IntelliJ IDEA, mas utilizo muito mais o eclipse, e algumas vezes até o NetBeans.
Todas as 3 IDEs suportam refactorings, e até o VIM e o Emacs suportam refactorings, mas não se iludam desenvolvedores Java, o pessoal da microsoft viu que isto era bom, e também adicionou suporte a diversos refactorings no visual studio, por tanto vocês não são mais os únicos com boas ferramentas.
Então respondendo a pergunta do título, não precisa decorar tudo, e não precisa fazer tudo na unha não, mas por favor, decore pelo menos os atalhos para os refactorings suportados pela sua IDE, o seu código agradece.
Outro dia eu coloco uma lista de atalhos de cada IDE para alguns refactorings por aqui (se der tempo :D )

Mas só isto basta? Meu código não vai parar de funcionar mesmo?

Um dos valores do Extreme Programming é a coragem, e este valor é necessário para se refatorar o código por exemplo, a probabilidade de o seu código parar de funcionar depois de um refactoring com a ajuda da sua IDE é bem pequena, um refactoring manual é mais arriscado, mas o código limpo bonito e funcional vale o risco.
Outra coisa, para diminuir o risco, escreva testes para o seu código sempre, com os testes, você tem algo para garantir que você não quebrou nada enquanto estava refatorando.
Mas se o mariquinhas ai não tem coragem de mexer no próprio código, pode continuar escrevendo código mediocre por ai, deve ter alguem com coragem o suficiente para fazer um trabalho decente e entregar código de qualidade na sua empresa, só espero que o seu chefe não se preocupe muito com qualidade, se não o teu medinho vai custar o teu emprego, por que com certeza, esta tua frescura já ta custando a qualidade do teu trabalho sua franguinha!

Sem brincadeiras agora, nem toda hora é hora de refatorar, nem todo refactoring vale a pena, nem sempre se tem tempo para melhorar o que já esta pronto, mas ler o código e saber o que pode ser melhorado, e como melhora-lo vai garantir que você faça menos porcaria no futuro, pelo menos comigo isto funciona :D

Tags: , ,

19 Nov 08 Curso básico de refactoring para quem é pobre e preguiçoso

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 :D )
Siga os passos abaixo:

  1. 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”
  2. 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
  3. 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”.
  4. 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”.
  5. Agora dentro do método “doIt”, podemos remover o try/catch.
  6. 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");
  7. 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.
  8. 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);
    }
  9. 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.
  10. 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.
  11. 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();
    }
  12. 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.
  13. 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.
  14. 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.
  15. 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. :D

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 :D

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?

Tags: , , ,

13 Nov 08 Comentário no código é para os fracos

Provavelmente serei crucificado por causa deste post, mas se você se der ao trabalho de ler até o final, provavelmente vai concordar comigo que comentários no código são para os fracos, programador hardcore de verdade escreve código legível!
É exatamente isto que eu estou dizendo, por exemplo, o que faz o código abaixo?

1
2
3
4
5
6
7
8
9
10
public String write(StringBuilder fle, StringBuffer con) {
  File f = new File(fle.toString());
  FileReader fr = new FileReader(f);
  BufferedReader br = new BufferedReader(fr);
  String lin;
  while((lin=br.readLine())!=null){
    con.append(lin).append("\n");
  }
  return con.toString();
}

Difícil? E este ainda é um código simples, mas vamos dar uma melhorada nele …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public String read(StringBuilder fle, StringBuffer con) {
  //Opens the file with the name container in the fle parameter
  File f = new File(fle.toString());
  //Create a file reader, then a buffered reader to make our work easier
  FileReader fr = new FileReader(f);
  BufferedReader br = new BufferedReader(fr);
  String lin;
  //Read each line of the file until it is null
  while((lin=br.readLine())!=null){
    //Put the content read into the buffer pointed by the parameter "con"
    con.append(lin).append("\n");
  }
  //The caller already have the content, because he created the buffer, but I'll return the string anyway
  return con.toString();
}

Mais fácil certo? Bastou ler os comentários, mas o código continua um lixo.
Ou seja, esta é uma gambiarra utilizada por péssimos programadores para contornar a própria limitação de não conseguir escrever um código decente.

Então qual a solução que eu recomendo?
Vamos tentar reescrever este método então:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public String readFileContents(File fileToRead) {
  boolean canReadFile = fileToRead.exists();
  if(!canReadFile)
    return "";
  StringBuilder buffer = new StringBuilder();
  BufferedReader readerForFile = openBufferedReaderForFile(fileToRead);
  readFileContetIntoBuffer(buffer,readerForFile);
  closeFileReader(readerForFile);
  return buffer.toString();
}
private  BufferedReader openBufferedReaderForFile(File fileToRead){
  return new BufferedReader(new FileReader(fileToRead));
}
private void readFileContetIntoBuffer(buffer,readerForFile){
  String line;
  while((line=readerForFile.readLine())!=null){
    buffer.append(line).append("\n");
  }
}
private void closeFileReader(readerForFile){
  readerForFile.close();
}

Agora se você prestar atenção no nome do método “readFileContents” já vai saber o que o método faz, Além disto, o código do método é quase legível em inglês. A leitura dele ficaria mais ou menos assim:

if not can read file, return null
open Buffered Reader For File: fileToRead
read File Contet Into Buffer: buffer, readerForFile
close File Reader: readerForFile
return buffer.toString();

Ou seja, qualquer um que entenda inglês, como qualquer desenvolvedor tem a obrigação de entender, vai ler o método como se fosse um comentário.

E eu já vi gente fazendo pior do que isto, o código tinha comentários, mas parecia com esta coisa ai em baixo:

1
2
3
4
5
public String write(StringBuilder fle, StringBuffer con) {
  File f = new File(fle.toString()); FileReader fr = new FileReader(f);  BufferedReader br = new BufferedReader(fr);
String lin; while((lin=br.readLine())!=null){  con.append(lin).append("\n");  }
  return con.toString();
}

Com certeza tinha muito menos linhas de código do que a minha versão :D
Mas não é uma tarefa fácil entender o código que uma criatura destas escreve :D

Claro que o exemplo que eu apresentei foi um exemplo bem simples, e que escrever código legível requer uma certa prática …
Então, vou fazer uma proposta:
Vou deixar um exemplo de código abaixo, e vocês tentam torna-lo mais legível. Em um ou dois dias eu posto a minha resposta aqui.
Quem quiser pode postar nos comentários o código que escreveu.
Para que o código fique colorido no blog, basta colocar dentro de uma tag <pre lang=”java” line=”1″> … </pre>

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);
	}
}

E agora um “main” só para executar o lixão acima.

1
2
3
4
5
6
7
8
9
package blog;
 
import java.io.IOException;
 
public class Main {
	public static void main(String[] args) throws IOException {
		new VeryBadlyNamedFile("c:\\anyFile.any",null,null).doIt();
	}
}

Os exemplos estão em java, mas em qualquer linguagem os comentários para explicar o código servem para mascarar a incapacidade dos programadores escreverem código decente.

PS.: Só para constar, eu não acho de verdade que vocês não devem comentar o código, mas se vocês não escrevem código legível, ou escrevem código que realmente precisa de um comentário para outro programador entender, então vocês não aprenderam a programar ainda!

PS2.: só para constar, o código deste post foi inventado na hora, inspirado em coisas que ja vi em diversos lugares por ai, masescrevi ele direto no blog, então existe uma grande possibilidade de não compilar.

PS3.: acho que preciso de exemplos melhores, mas vocês devem ter entendido a idéia deste post

Tags: , , ,