Escrever um bom código, legível, eficiente e seguro, é uma competência necessária para codificar em qualquer linguagem. O objetivo é abordar técnicas de refatoração, boas práticas, código seguro e testes, utilizando exemplos com a linguagem Java. A bibliografia indicada é Refatoração, do Martin Fowler, e Effective Java do Joshua Bloch.
3. Apresentação
“Quem é Márcio Torres?”
●
Primeiro contato com um computador na década de
90, um CP 500 com Basic embedded;
●
Desenvolvedor dBase e Clipper;
●
Instrutor de Informática na Degraus;
●
Suporte e Manutenção;
●
Instrutor de Informática no Senac;
●
Desenvolvedor Java;
●
Professor no IFRS nos cursos técnico e tecnólogo;
4. Introdução ● Práticas transversais:
O que veremos e por que é importante? ● Fazendo um bom design;
● Conversaremos sobre: ● Aderir a convenções;
● Boas práticas lidando com: Strings, ● Seguir princípios de design
Números, Coleções; orientado a objetos;
● Como lidar com: Nulos, Exceções,
Passagem de parâmetros;
● Problemas típicos de domínio, assinaturas
de métodos;
● Como melhorar o código aplicando
refatorações;
● É importante por que:
● Torna a aplicação mais resistente a falhas;
● Deixa o código mais fácil de ler;
● A aplicação fica mais robusta e rápida;
● O mercado busca bons profissionais;
5. É sobre requisitos não funcionais ...
Performance
Extensibilidade
Responsividade
Manutenibilidade
Robustez
Legibilidade
Confiabilidade
Testabilidade
… e qualidade interna.
6. Mãos a obra
●
Exemplos práticos, com
embasamento técnico e/ou
bibliográfico;
7. Lidando com Strings ...
Como instanciar Strings
String q = new String("sim"); As instâncias de Strings literais
são armazenadas em um espaço
if (q == "sim") {
de memória chamado Permanent
System.out.println("SIM!!!");
Generation. Elas são reusadas,
} else { com a exceção de quando é
System.out.println("NÃO??!! COMO NÃO?!!"); usado o operador new, que por
consequência cria dois objetos.
}
String q = "sim";
O operador == compara
igualdade da variável, não if ("sim".equals(q)) {
dos objetos. Sempre use System.out.println("SIM!!!");
equals, e prefira passar a } else {
variável como parâmetro
System.out.println("NÃO??!! COMO NÃO?!!");
do equals.
}
Entenda como a memória é gerenciada na plataforma Java
9. Lidando com Strings ...
Como concatenar Strings
String nomes = “Nomes: “;
for (int i = 1; i < 1000000; i++) {
Use StringBuilder para
concatenar muitas Strings, é mais
nomes = nomes + “, “ + getNome();
rápido e usa menos memória
}
Cada resultado com
o operador + gera
uma nova String StringBuilder nomes = new StringBuilder(“Nomes: “);
for (int i = 1; i < 1000000; i++) {
nomes.append(“, “).append(getNome());
}
Conheça a API do Java, estudea.
10. Lidando com Números ...
Como declarar membros numéricos Tipos primitivos consomem
class PlanoDesconto { menos memória, além de ter um
Double rendaMinima; footprint menor
Double rendaMaxima;
Integer idadeMinima;
Integer idadeMaxima;
class PlanoDesconto {
double rendaMinima;
Tipos primitivos reduzem a necessidade double rendaMaxima;
de boilerplate code int idadeMinima;
int idadeMaxima;
public Double getValorMensalComDesconto() {
double _valorMensal = valorMensal == null ? 0 : valorMensal;
double _valorDesconto = valorDesconto == null ? 0 : valorDesconto;
return _valorMensal - _valorDesconto;
}
Prefira tipos primitivos sempre que public double getValorMensalComDesconto() {
possível, mas cuidado com o return valorMensal - valorDesconto;
Primitive Obsession Antipattern! }
11. Evitando NullPointerException's ...
Escrevendo métodos seguros
public List<Foto> getFotos1(String ordem, final Integer direcao) {
if (ordem.equals("descricao")) {
Collections.sort(fotos, new Comparator<Foto>() {
@Override
public int compare(Foto o1, Foto o2) {
if (direcao == 1) {
return o1.getDescricao().compareTo(o2.getDescricao());
} else if (direcao == 2) {
return o2.getDescricao().compareTo(o1.getDescricao());
}
return 0;
public List<Foto> getFotos2(String ordem, final Integer direcao) {
if ("descricao".equals(ordem)) {
Collections.sort(fotos, new Comparator<Foto>() {
@Override
public int compare(Foto o1, Foto o2) {
if (new Integer(1).equals(direcao)) {
return o1.getDescricao().compareTo(o2.getDescricao());
} else if (new Integer(2).equals(direcao)) {
return o2.getDescricao().compareTo(o1.getDescricao());
}
return 0;
Evite NPE escrevendo métodos testados e seguros, pensando:
“E se o parâmetro vier nulo? E se o valor recebido não é tratado?”
12. Escrevendo métodos seguros e
intuitivos ... public List<Foto> getFotos(Ordem ordem) {
return getFotos(ordem, null);
Sobrecarregue métodos ao invés
}
de passar nulos para parâmetros
opcionais
public List<Foto> getFotos(Ordem ordem, final Direcao direcao) {
if (Ordem.DESCRICAO.equals(ordem)) {
Collections.sort(fotos, new Comparator<Foto>() {
@Override
public int compare(Foto o1, Foto o2) {
if (Direcao.DESCENDENTE.equals(direcao)) {
return o2.getDescricao().compareTo(o1.getDescricao());
}
return o1.getDescricao().compareTo(o2.getDescricao());
Não use Strings onde outro tipo é mais adequado
Avalie definir um comportamento default para o caso de omissão …
Desenhe para que nulos não sejam passados como parâmetros ...
13. Escrevendo métodos legíveis ...
class BadReportPreview {
Não parece melhor chamar o
public void show(String dados,
preview na impressora assim: boolean impressora) {
if (impressora) {
preview.show(dados, Destino.IMPRESSORA);
// mostra na impressora
} else {
Do que assim: // mostra no monitor
}
preview.show(dados, true); }
}
class GoodReportPreview {
Avalie usar public enum Destino {
TELA, IMPRESSORA;
enumerados }
para passar public void show(String dados, Destino destino) {
no método if (Destino.TELA.equals(destino)) {
// mostra na tela
ao invés de } else if (Destino.IMPRESSORA.equals(destino)) {
boleanos ... // mostra na impressora
} else {
throw new
IllegalArgumentException("Destino nao foi informado");
}
}
}
14. Convenções para métodos ...
Entendendo algumas convenções
public void ordernarPorTitulo(List<Livro> livros) {
Collections.sort(livros, new Comparator<Livro>() {
@Override
public int compare(Livro o1, Livro o2) {
return o1.getTitulo().compareTo(o2.getTitulo());
}
});
}
public List<Livro> ordernarPorTitulo(List<Livro> livros) {
List<Livro> copia = new ArrayList<Livro>(livros);
Collections.sort(copia, new Comparator<Livro>() {
@Override
public int compare(Livro o1, Livro o2) {
return o1.getTitulo().compareTo(o2.getTitulo());
}
});
return copia;
}
Métodos tem retorno quando retornam uma nova instância ...
Se o método altera a instância parametrizada use void …
Cuidado com os métodos que trazem efeitos colaterais, isto é, alteram a
instância parametrizada ...
15. Lidando com coleções ...
Implementado os métodos necessários ...
class Foto {
...
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Foto other = (Foto) obj;
if (id != other.id) return false;
return true;
}
Sempre sobrescreva equals e hashCode nas classes em que suas instâncias
participarão de coleções, especialmente baseadas em Hash.
Siga o contrato. Lembre: objetos considerados iguais devem ter o mesmo
hashCode.
17. Lidando com exceções ...
Fazer um design com ou sem exceções ?
public Conta find(Integer id) {
if (id == null) {
throw new IllegalArgumentException("Id inválido",
new NullPointerException("ID nulo"));
}
return contas.get(id);
}
public Conta find(Integer id) {
if (id == null) return null;
return contas.get(id);
}
public Conta load(Integer id) throws RegistroNaoEncontradoException {
if (id == null) throw new RegistroNaoEncontradoException("ID nulo");
Conta c = contas.get(id);
if (c == null) throw new RegistroNaoEncontradoException();
return c;
}
Lance exceções só quando necessário, exceções diminuem a performance ...
Se for o caso, propague o stacktrace e documente bem …
Não ignore as exceções na hora de tratálas...
Use Checked Exceptions para recuperáveis e Runtime Exceptions para falhas do
sistema ...
18. Lidando com Three Valued Logic ...
A maldição herdada pelos SGBD's
for (Conta c : contas) {
if (c.isEspecial() != null && c.isEspecial()) {
contasEspeciais++;
}
}
for (Conta c : contas) {
if (c.isEspecial()) {
contasEspeciais++;
}
}
Reduza a necessidade de Boiler Plate Code …
Use primitivos, ou trate na classe …
Pense qual é o valor por omissão ...
19. Refatorações ...
Detectar mau cheiro no código …
Melhorar a qualidade do código …
Não alterar o comportamento ...
Alguns maus cheiros:
Cadeias de mensagens ...
Campo temporário ...
Classes, métodos grandes ...
Método Longo ...
Lista longa de parâmetros ...
Alguns técnicas de refatoração:
Extrair método;
Extrair classe;.
Renomear método;
Renomear atributo;
Objeto parâmetro;
Usar fábrica ao invés de construtor;
Usar constante ao invés de número mágico;
20. Renomeando métodos e atributos ...
Use nomes expressivos ...
for (Conta c : c1) {
if ( ! c2.contains(c1)) {
webService.envComAval(c1);
}
}
for (Conta contaNaBase : contasNaBaseDados) {
if ( ! contasNoERP.contains(contaNaBase)) {
webService.enviaContaParaComissaoAvaliadora(contaNaBase);
}
}
Sempre que for adicionar um comentário, perguntese:
“Como posso melhorar o código ao ponto deste comentário não ser
mais necessário?”
Conta c = new Conta(); Conta c = new Conta(); Conta c = new Conta();
c.numero = 66410; c.numero = 66410; c.numero = 66410;
c.tipo = 1; c.tipo = Conta.FISICA; c.tipo = TipoConta.FISICA;
Não use magic numbers, prefira enumerados ...
21. Extrair método ...
Evite métodos muito longos ...
public static void processaContas() {
ContaRepository contaRepository = new ContaRepository();
List<Conta> c1 = contaRepository.findContas();
Collections.sort(c1, new Comparator<Conta>() {
@Override
public int compare(Conta o1, Conta o2) {
if (o1.numero > o2.numero) return 1;
if (o2.numero < o1.numero) return -1;
return 0;
}
});
// ...
public static void processaContas() {
ContaRepository contaRepository = new ContaRepository();
List<Conta> contas = contaRepository.findContas();
ordena(contas);
// ...
Divida funcionalidades usando métodos privados, de preferência reusáveis ...
22. Mais refatorações ...
●
Usar fábrica ao invés de construtor ...
●
Trocar número mágico por constante ...
●
Introduzir variável explicativa ...
●
Usar objeto parâmetro …
●
E várias outras ...
24. Bibliografia Recomendada
● Estes livros estão entre os melhores na bibliografia para quem já
programa com Java (e outras linguagens) e deseja tornarse um
desenvolvedor melhor.