Refatoração e Boas Práticas no Desenvolvimento de Software com a Linguagem Java - Márcio Josué...
-
Upload
tchelinux-slides -
Category
Technology
-
view
2.916 -
download
1
description
Transcript of Refatoração e Boas Práticas no Desenvolvimento de Software com a Linguagem Java - Márcio Josué...
Refatoração e Boas Práticas no Desenvolvimento de Software com Java
Márcio Torres
Agenda
● Apresentação● Introdução● Mãos a obra!● Bibliografia
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;
IntroduçãoO que veremos e por que é importante?
● Conversaremos sobre:● Boas práticas lidando com: Strings,
Números, Coleções;● 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;
● Práticas transversais:
● Fazendo um bom design;● Aderir a convenções;● Seguir princípios de design
orientado a objetos;
Performance
Robustez
Manutenibilidade
Testabilidade
Responsividade
ConfiabilidadeLegibilidade
É sobre requisitos não funcionais ...
… e qualidade interna.
Extensibilidade
● Exemplos práticos, com embasamento técnico e/ou bibliográfico;
Mãos a obra
Lidando com Strings ...Como instanciar Strings
Entenda como a memória é gerenciada na plataforma Java
String q = "sim";
if ("sim".equals(q)) {
System.out.println("SIM!!!");
} else {
System.out.println("NÃO??!! COMO NÃO?!!");
}
String q = new String("sim");
if (q == "sim") {
System.out.println("SIM!!!");
} else {
System.out.println("NÃO??!! COMO NÃO?!!");
}
As instâncias de Strings literais são armazenadas em um espaço de memória chamado Permanent Generation. Elas são reusadas, com a exceção de quando é usado o operador new, que por consequência cria dois objetos.
O operador == compara igualdade da variável, não dos objetos. Sempre use equals, e prefira passar a variável como parâmetro do equals.
Organização da memória na Máquina Virtual Java da Sun Oracle
Lidando com Strings ...Como concatenar Strings
Conheça a API do Java, estudea.
String nomes = “Nomes: “;
for (int i = 1; i < 1000000; i++) {
nomes = nomes + “, “ + getNome();
}
StringBuilder nomes = new StringBuilder(“Nomes: “);
for (int i = 1; i < 1000000; i++) {
nomes.append(“, “).append(getNome());
}
Use StringBuilder para concatenar muitas Strings, é mais rápido e usa menos memória
Cada resultado com o operador + gera uma nova String
Lidando com Números ...Como declarar membros numéricos
Prefira tipos primitivos sempre que possível, mas cuidado com o Primitive Obsession Antipattern!
Tipos primitivos consomem menos memória, além de ter um footprint menor
class PlanoDesconto {
Double rendaMinima;Double rendaMaxima;Integer idadeMinima;Integer idadeMaxima; class PlanoDesconto {
double rendaMinima;double rendaMaxima;int idadeMinima;int idadeMaxima;
Tipos primitivos reduzem a necessidade de boilerplate code
public double getValorMensalComDesconto() {
return valorMensal - valorDesconto;
}
public Double getValorMensalComDesconto() {double _valorMensal = valorMensal == null ? 0 : valorMensal;double _valorDesconto = valorDesconto == null ? 0 : valorDesconto;return _valorMensal - _valorDesconto;
}
Evitando NullPointerException's ...Escrevendo métodos seguros
Evite NPE escrevendo métodos testados e seguros, pensando:“E se o parâmetro vier nulo? E se o valor recebido não é tratado?”
public List<Foto> getFotos1(String ordem, final Integer direcao) {if (ordem.equals("descricao")) {
Collections.sort(fotos, new Comparator<Foto>() {@Overridepublic 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>() {@Overridepublic 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;
Não use Strings onde outro tipo é mais adequadoAvalie definir um comportamento default para o caso de omissão …Desenhe para que nulos não sejam passados como parâmetros ...
Escrevendo métodos seguros e intuitivos ...
public List<Foto> getFotos(Ordem ordem, final Direcao direcao) {
if (Ordem.DESCRICAO.equals(ordem)) {Collections.sort(fotos, new Comparator<Foto>() {
@Overridepublic int compare(Foto o1, Foto o2) {
if (Direcao.DESCENDENTE.equals(direcao)) {return o2.getDescricao().compareTo(o1.getDescricao());
}
return o1.getDescricao().compareTo(o2.getDescricao());
public List<Foto> getFotos(Ordem ordem) {
return getFotos(ordem, null);
}Sobrecarregue métodos ao invés de passar nulos para parâmetros opcionais
Escrevendo métodos legíveis ...class BadReportPreview {
public void show(String dados,boolean impressora) {
if (impressora) {// mostra na impressora
} else {// mostra no monitor
}}
}
class GoodReportPreview {
public enum Destino {TELA, IMPRESSORA;
}
public void show(String dados, Destino destino) {if (Destino.TELA.equals(destino)) {
// mostra na tela} else if (Destino.IMPRESSORA.equals(destino)) {
// mostra na impressora} else {
throw new IllegalArgumentException("Destino nao foi informado");
}}
}
Não parece melhor chamar o preview na impressora assim:preview.show(dados, Destino.IMPRESSORA);
Do que assim:
preview.show(dados, true);
Avalie usar enumerados para passar no método ao invés de boleanos ...
Convenções para métodos ...Entendendo algumas convenções
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 ...
public List<Livro> ordernarPorTitulo(List<Livro> livros) {List<Livro> copia = new ArrayList<Livro>(livros);Collections.sort(copia, new Comparator<Livro>() {
@Overridepublic int compare(Livro o1, Livro o2) {
return o1.getTitulo().compareTo(o2.getTitulo());}
});return copia;
}
public void ordernarPorTitulo(List<Livro> livros) {Collections.sort(livros, new Comparator<Livro>() {
@Overridepublic int compare(Livro o1, Livro o2) {
return o1.getTitulo().compareTo(o2.getTitulo());}
});}
Lidando com coleções ...Implementado os métodos necessários ...
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.
class Foto {...
@Overridepublic int hashCode() {
final int prime = 31;int result = 1;result = prime * result + id;return result;
}
@Overridepublic 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;
}
Lidando com coleções ...Devolva listas vazias invés de nulo. Isto fará com que o cliente de sua API não tenha que escrever qualquer boilerplate code;
for (Livro livro : livros) {
if (livro.getKeywords() != null && livro.getKeywords().contains(k)) {count++;
}
}
for (Livro livro : livros) {
if (livro.getKeywords().contains(k)) {count++;
}
}
É possível instanciar coleções na criação da classe, que tem uma performance pior. Se quiser melhorar a performance pode fazer Lazy Initialization ou atuar como delegado da coleção (melhor aproximação);
Lidando com exceções ...Fazer um design com ou sem exceções ?
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 ...
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;
}
public Conta find(Integer id) {if (id == null) return null;return contas.get(id);
}
public Conta find(Integer id) {if (id == null) {
throw new IllegalArgumentException("Id inválido",new NullPointerException("ID nulo"));
}return contas.get(id);
}
Lidando com Three Valued Logic ...A maldição herdada pelos SGBD's
Reduza a necessidade de Boiler Plate Code …Use primitivos, ou trate na classe …Pense qual é o valor por omissão ...
for (Conta c : contas) {if (c.isEspecial() != null && c.isEspecial()) {
contasEspeciais++;}
}
for (Conta c : contas) {if (c.isEspecial()) {
contasEspeciais++;}
}
Refatorações ...
Alguns maus cheiros:Cadeias de mensagens ...Campo temporário ...Classes, métodos grandes ...Método Longo ...Lista longa de parâmetros ...
Detectar mau cheiro no código …Melhorar a qualidade do código …Não alterar o comportamento ...
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;
Renomeando métodos e atributos ...Use nomes expressivos ...
Não use magic numbers, prefira enumerados ...
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();c.numero = 66410;c.tipo = 1;
Conta c = new Conta();c.numero = 66410;c.tipo = Conta.FISICA;
Conta c = new Conta();c.numero = 66410;c.tipo = TipoConta.FISICA;
Extrair método ...Evite métodos muito longos ...
Divida funcionalidades usando métodos privados, de preferência reusáveis ...
public static void processaContas() {ContaRepository contaRepository = new ContaRepository();List<Conta> c1 = contaRepository.findContas();
Collections.sort(c1, new Comparator<Conta>() {@Overridepublic 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);
// ...
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 ...
Na primeira vez, apenas implemente, lide com as duplicações mais evidentes, mas na terceira vez que fizer algo semelhante, refatore!
Martin Fowler
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.
http://www.refactoring.com/catalog/http://c2.com/xp/CodeSmell.htmlhttp://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOodhttp://marciojrtorres.blogspot.com/