Seja um Desenvolvedor, não um liquidificador

Olá, tudo bem?

Vivemos em uma época onde é muito fácil de criar projetos com frameworks “mágicos”. O que é ensinado em diversos blogs e faculdades é que: “basta anotar com isso aqui que está pronto”.

Anotações tem a função de facilitar a utilização e a identificação de comportamentos dos frameworks, mas existem desenvolvedores que se perdem com essa facilidade e acabam esquecendo de conceitos básicos.

CRUD, filas JMS, Beans, controle de transações podem ser criados em minutos, mas e a parte de Orientação a Objeto? Conceitos básicos do Java? O que tem acontecido para que as boas práticas que demoraram anos para serem desenvolvidas simplesmente serem abandonadas?

Misturando Conceitos

Para ser mais claro, vamos ver alguns exemplos de códigos postados por desenvolvedores em fóruns. No primeiro exemplo é possível ver a mistura de JPA com JSON:

@Entity
@XmlRootElement
@JsonAutoDetect
@DiscriminatorColumn(name = "animal_type")
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Dog.class, name = "dog"),
        @JsonSubTypes.Type(value = Cat.class, name = "cat"),
        @JsonSubTypes.Type(value = Bird.class, name = "bird")
})
public class Animal {

    @Id
    private int id;

    @JsonSerialize(using = JsonDateSerializer.class)
    private Date birthday;

    @JsonView(ToothView.class)
    @OneToMany(mappedBy = "animal")
    private List<Tooth> teethList;

    // other stuff
}

@Entity
@JsonRootName(value = "Dog")
@JsonIgnoreProperties(ignoreUnknown = true)
public class Dog extends Animal {

    @JsonProperty
    private String name;

    @Convert(converter = JsonAttributeConverter.class)
    private Double weight;

    @JsonIgnore
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "dog", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Bone> boneList;

    public Dog() {
    }
}

Qual o problema do código acima? Qual o motivo da mistura de anotações de JSON (view) com anotações do JPA (modelo)? Note que, por causa dessa mistura, qualquer campo que a view deva exibir de modo diferente afetará justamente a mesma classe utilizada para salvar dados no banco de dados.

Não respeitando as camadas

Vamos a outro exemplo:

@ViewScoped
@ManagedBean
@TransactionManagement(TransactionManagementType.BEAN)
public class ManagedBeanOne extends SuperManagedBean implements Serializable {
     // attributes
    public String doSomething() {
        try{
            transaction.begin(); 
            // do something
            transaction.commit();
        }
        catch(Exception ex){
            transaction.rollback();
        }
    }
}


@ManagedBean
@WebService
@ViewScoped
public class ManagedBeanTwo implements Serializable{}

Por que fazer de um ManagedBean de JSF também como um EJB? E até pior, por que deixar que um ManagedBean controle uma transação? Qual o ganho em deixar o ManagedBean com diversas responsabilidades?

Tamanho mais atrapalha que ajuda

Um exemplo que eu não tive como colocar aqui foi de uma classe com mais de 1k de linhas. A pior parte é saber que esse tipo de classe fica sempre como motivo de chacota, ou essa classe é tratada como um objeto de terror e espanto pois qualquer alteração quebra o sistema.

Até quando os desenvolvedores vão ter o hábito de sempre querer adicionar um if sem antes fazer um pequeno refactoring?

Exception que é Chefe

Outra prática terrível é deixar uma exceção controlar o fluxo. Veja como é fácil cometer esse erro:

try{
    myObject.doSomething();
} catch(MyExceptionOne one){
    sendErrorSmsToCustomer();
    defineOldPlanToCustomer();
    sentToScreenOne();
} catch(MyExceptionTwo two){
    sendErrorEmailToCustomer();
    defineNewPlanToCustomer();
    sentToScreenTwo();
}

No exemplo acima, o que aconteceria com os dados do banco se na primeira linha de uma exceção acontecesse um erro? O que aconteceria se algum desenvolvedor fosse executar a chamada myObject.doSomething(); em alguma outra classe, e não tratasse todos os erros como no exemplo acima? E se ele não adicionasse a linha onde não trata o plano do usuário?

E agora?

Falar dos problemas é fácil, mas eu creio que ser necessário mostrar soluções que podemos aplicar em nosso dia a dia para melhorar a qualidade de código. O que vamos ver nas próximas páginas não é nada inventado por mim, mas são práticas extraídas de livros sobre design patterns e boas práticas de programação.

O que veremos:

  • Página 2: Não transpasse camadas
    • Atenção nos pacotes
  • Página 3: Aumente a coesão
  • Página 4: Reduza o acoplamento
    • Facade é uma boa pedida
  • Página 5: Voltemos ao mundo dos DTOs
    • Como facilitar a cópia de uma Entity para um DTO
  • Página 6: Métodos e Classes pequenas
  • Página 7: Como usar exceção corretamente?
  • Página 8: Teoria da Janela quebrada
    • Mantenha seu build sempre funcionando
    • Nunca deixe testes quebrados
    • Não comente/ignore seus testes
  • Página 9: Boas práticas que ajudam
    • KISS
    • DRY
  • Página 10: Quantidade de testes não importa, mas sim a qualidade
    • Utilize ferramentas
    • Achou um bug? Crie um teste
  • Página 11: Conclusão

Espero que este post seja útil ao seu dia a dia. (:

Não poderia deixar de agradecer ao Rodrigo Sasaki (http://cv.rodrigosasaki.com/) que me deu uma força absurda na revisão do post.

35 thoughts on “Seja um Desenvolvedor, não um liquidificador

  1. Ótimo assunto a ser abordado em uma época em que annotations invadem nossos códigos e cada vez mais é notável a falta de conceitos básicos

    • Rafael, bom dia.

      Realmente eu fico triste por ver o básico sendo trocado por facilidades, que depois viram o buraco do projeto.

      Obrigado pelo apoio. (:

  2. Hébert, você nunca retorna uma entidade JPA para a view, mesmo se for para um HTML? Sempre cria um DTO se for fazer isso?

    Fiquei surpreso com a afirmação, não estava ciente de que isso era considerado uma boa prática. Costumo criar DTOs apenas se as informações da view não refletem exatamente os campos da entidade, necessitando exibir outros campos.

    Outra coisa, e se eu estiver utilizando JDBC? Ainda assim você criaria um DTO para exibir na view?

    • Rafael, boa noite.

      Nos últimos 4 projetos que criei eu não retornei mais a Entity para a VIEW, e honestamente só vi ganho nessa abordagem. O problema de começar retornando uma Entity direto para View é justamente que quando houver a necessidade da diferença em que a view precisa de coisa a mais, será necessário um refactoring que poderia ser evitado.
      E eu acredito que a prática de DTO deve ser aplicada independente se você está com JDBC, JPA, etc.

      Obrigado pela visita. (:

      • E como você nomeia estes DTOs?
        Por exemplo, digamos que você tenha uma página ListaProdutos.jsp, e outra CadastraProduto.jsp, sendo que existem diferenças entre os campos exibidos em cada uma.

        Neste caso você criaria uma CadastraProdutoDTO e uma ListaProdutosDTO, ou criaria apenas uma ProdutoDTO com todos os campos das duas páginas?

        • Rafael, boa noite.

          Eu diria isso varia. No caso de um cadastro de produto, realmente eu utilizaria um ProdutoDTO. Agora, imagine que na tela de listar produto, seja necessário diversos outros atributos. Aí eu criaria um ListarProdutosHome e nele colocaria o ProdutoDTO e os outros atributos.

          Para entrada de dados que eu costumo criar objetos mais específicos. Imagine que tenha um serviço só para alterar o preço do produto. Eu penso que seria melhor ter um AlteraPreçoProdutoDTO com um campo apenas (+ID), do que a pessoa ver um ProdutoDTO e não saber oq deve ou não ter lá dentro.

          Espero ter sido claro.

          Obrigado pela visita.

  3. Ótimo post, para mim que sou um “recruta” em programação java o post despertou uma vontade de buscar mais sobre as boas práticas de desenvolvimento e principalmente sobre o que não deve ser feito!

    Abraços

    • Matheus, boa noite.

      Fico feliz em saber que consegui te estimular na qualidade de código.

      Obrigado pelo comentário e pelo apoio.

  4. otimo post hebert como sempre

    mas eu gostaria de fazer uma pergunta esse post voce escreveu baseado em sua experiencia como desenvolvedor ou se voce leu algum livro ou algo assim que serviu de inspiração para criar ele ? e se foi so de experiencia qual livro ou conteudo voce indicaria pra ter uma base maior alem do post ?

    • Manoel, boa noite.

      Eu diria que foi baseado em um pouco de cada.

      Quanto aos livros eu já li + de 40 livros técnicos, então não tenho um específico que aborde tudo. =/

      Eu diria que o livro de padrão de projeto e de OO da séria Use a Cabeça já são um ótimo início.

      Obrigado pelo apoio e pelo comentário.

  5. Excelente artigo Hebert. A facilidade fornecida pelo mundo das annotations e a pressa por “criar” algo as vezes deixa o desenvolvedor preguiçoso por entender como as coisas funcionam por debaixo dos panos e a forma correta de se estruturar uma aplicação. Conhecer o conceito é parte fundamental da evolução do desenvolvedor e, o resultado vemos em sistemas que chegam a ir para produção mas logo começam a falhar por coisas como essas.

  6. Herbert, uma pergunta,no caso do componente p:autocomplete do primefaces, para retornar a lista com os valares para o componente autocompletar, faço acesso ao um metodo da minha classe DAO, isso é correto ou estou misturando camadas:

    /* Métodos dos Componentes AutoComplete */
    public List orgOrigemComplete(String parametro) {
    return new TOrgDAO().consultarComplete(parametro, “I”,
    loginBean.getLogin());
    }

    Coloco no meu Managed Bean ….

    • Cedric, boa noite.

      Note que seu MB está acoplado ao DAO diretamente. Tem um parâmetro “I” que não é claro no que significa e o MB também chamada o loginBean.

      Honestamente eu faria lago como
      return pessoaService.consultar(parametro, login());

      Desse modo, qualquer alteração no modo de pesquisa, não afetaria o MB.

      Bem, essa é minha opinião e existem vários outros modos de se fazer isso.

      Espero ter ajudado.

  7. Excelente post Hebert, porém eu gostaria de discutir um pouco sobre a camada de Service:
    Ao implementação de pequenos CRUDs na aplicação os objetos Service acabam existindo como meros Dispatchers, isso acaba entrando em outro problema, pois criamos um objeto anêmico.

    • Leonardo, boa tarde.

      Me desculpe a demora, estive muito ocupado nesse último mês.

      Eu concordo com você. O problema é justamente quando for começar a ter alguma lógica. A pessoa que for criar a lógica, ela vai saber criar a nova camada? Se sim, por mim poderia até pular essa camada. Em minha experiência é que se os desenvolvedores não vêem, eles não usam/criam do padrão. Por isso que eu acabo sempre criando essa camada ‘seca’. =/

      Obrigado por tudo e desculpe a demora.

Leave a Reply to cedric williams Cancel reply