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çã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;
É sobre requisitos não funcionais ...

Performance
                                          Extensibilidade


              Responsividade
                                                            Manutenibilidade

                               Robustez


      Legibilidade
                                                    Confiabilidade



                                   Testabilidade



… e qualidade interna.
Mãos a obra


●
    Exemplos práticos, com 
    embasamento técnico e/ou 
    bibliográfico;
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
Organização da memória na 
Máquina Virtual Java da Sun Oracle
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, estude­a.
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!            }
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?”
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 ...
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");
                            }
                        }

                   }
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  ...
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.
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 ?
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 ...
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 ...
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;
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, pergunte­se:
“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 ...
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 ...
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 tornar­se um 
    desenvolvedor melhor.
http://www.refactoring.com/catalog/
http://c2.com/xp/CodeSmell.html
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
http://marciojrtorres.blogspot.com/




                                     marciojrtorres@gmail.com

Refatoração e Boas Práticas no Desenvolvimento de Software com a Linguagem Java - Márcio Josué Ramos Torres

  • 1.
  • 2.
    Agenda ● Apresentação ● Introdução ● Mãos a obra! ● Bibliografia
  • 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 requisitosnã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
  • 8.
  • 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, estude­a.
  • 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(Stringordem, 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.
  • 16.
    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);
  • 17.
    Lidando com exceções ... Fazer um design com ou sem exceções ? public Conta find(Integerid) { 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, pergunte­se: “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 voidprocessaContas() { 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 ...
  • 23.
  • 24.
    Bibliografia Recomendada ● Estes livros estão entre os melhores na bibliografia para quem já  programa com Java (e outras linguagens) e deseja tornar­se um  desenvolvedor melhor.
  • 25.