Todos os métodos devem ser públicos? Definitivamente não!

Todos os métodos devem ser públicos? Definitivamente não!

1 de julho de 2009  |  Opinião, Ruby  | 

Recentemente fiz a seguinte pergunta no Twitter:

Picture 1

@carlosbrando

As respostas que recebi apenas confirmaram o que eu já imaginava.

Picture 2

@tapajos

Picture 4

@flaviogranero

O motivo da pesquisa é que recentemente eu estava lendo um artigo onde o autor argumentava que o ideal seria criarmos classes somente com métodos públicos, evitando ao máximo métodos privados ou protegidos. E entre outros argumentos, um dos motivos para se fazer isto era simplificar a criação de testes.

Uma coisa que me preocupa no Ruby é que é muito comum encontrar programadores que não estão nem um pouco preocupados se os seus métodos são públicos, protegidos ou privados. Pior ainda, quando tomam decisões apoiando-se em argumentos como o acima.

Um clássico exemplo são os métodos de callback de validações do Active Record, que em praticamente 99% dos casos deveriam ser privados ou protegidos, mas é normal encontrá-los entre os métodos públicos de um modelo na maioria dos projetos Rails. Acredito que isto aconteça porque programadores iniciantes entusiasmados com as facilidades do Active Record apenas saem incluindo seus códigos sem pensar muito no que estão fazendo.

Eu também não tenho o hábito de testar métodos privados, pelas mesmas razões mencionadas pelos amigos do Twitter. Mas no Ruby, se você realmente desejar, é trivial realizar uma chamada em um método privado de um objeto. Uma das técnicas mais usadas por “testadores de métodos privados” é se valer das características dinâmicas da linguagem para transformar todos os métodos privados de uma classe em métodos públicos durante os testes. Diferente de outras linguagens, como C# por exemplo, onde você tem uma barreira formal para impedi-lo de acessar métodos privados, no Ruby é ridiculamente simples ultrapassar esta barreira.

Pensando desta maneira, definir um método como privado no Ruby pode não significar muita coisa, já que outro programador pode alterar esta característica com facilidade. Mas ainda assim, definir um método como privado é como dizer aos outros programadores que aquele método faz parte do funcionamento interno daquela classe e que o programador original quer se manter no direito de realizar qualquer tipo de alteração necessária sem se preocupar se alguém está usando ou não aquele método. Isto tem tudo a ver com encapsulamento, e pode fazer toda a diferença quando surgir a necessidade de alterar algum comportamento do objeto.

Toda vez que você constrói um novo objeto, você está construindo uma nova API. Cabe a você então decidir o que deve ser exposto e o que faz parte dos mecanismos internos deste objeto. Desta forma, quando surgir a necessidade de alterar alguma coisa, você não precisará se preocupar tanto com quantas classes serão afetadas pela mudança. Ao marcar um método como privado, você está se resguardando e garantindo que não causará danos ao restante do sistema, já que você está dentro da fronteira do encapsulamento, podendo assim refatorar seu código sem dor na consciência.

Por favor, não saia simplesmente injetando código em suas classes. Pense!


10 Comentários


  1. Já tive esse problema há um certo tempo. Lembro ter dado uma boa pesquisada sobre o assunto. Acontece que eu tinha uma classe que tinha um método público e vários privados que eram “orquestrados” por esse método público. Ou seja, na verdade eu tava colocando muita lógica dentro dos métodos privados e por isso fiquei com vontade/necessidade de testá-los.

    O meu erro nesse caso era que essa classe estava quebrando o SRP (Single Responsibility Principle). Eu tive que extrair então alguns desses métodos privados para novas classes, tornando-os públicos, de modo que poderia testá-los sem ficar com a pulga atrás da orelha.

    Um bom modo também de ver quando métodos privados devem existir, é quando eles aparecem devido a um refactoring de um método público, como métodos “helpers” ou algo assim. Nesse caso um bom exemplo é o kata do bowling game do Uncle Bob (http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata).

  2. Como o Hugo já disse aí em cima, ter métodos privados demais é sinal de que você está colocando muita coisa no lugar errado.

    Se você segue técnicas como BDD/TDD isso é ainda mais aparente, porque antes de criar o seu método no objeto você vai criar o spec/teste mostrando como aquele método é utilizado e o método já nasce público. Não faz muito sentido você criar do nada um método privado dentro de um objeto, a não ser que você esteja fazendo refactoring de alguma coisa lá.

  3. No mundo OO, os objetos tem ações ou operações, nos quais são compostos de metodos, acontece que muitas vezes confundimos isso com metodo = ação, o que nem sempre é verdade… por isso criar metodos privados para operações de objetos é até natural, o problema acontece quando existe uma explosão deste metodos numa classe.quanto aos testes, cabe aqui o bom senso, até por que muitos metodos privados são testados quando se testar o metodo publico.

    obs:carlos sou de natal,será um grande prazer recebe-lo em minha cidade, você vai ver um paraiso!! :)

  4. “Definitivamente não!” é a expressão correta para essa prática. Quando conheci Ruby e o Rails, de fato, fiquei bastante empolgado com a simplicidade do código. E cometi os erros citados. Com o tempo e um pouco mais de experiência, percebi esses erros e os corrigi. E em relação aos testes, vale muito bem o comentário do Tapajós, “somente as interfaces públicas”, ou seja, os métodos público que fazem chamada para os métodos internos à classe.

  5. Oi Carlos,

    acho que o importante é testar o resultado de operações, ou seja, testar os métodos públicos já com o resultado que das operações dos métodos privados e protegidos. Não vejo razão de testar os métodos “não-públicos” até porque de toda forma, eles mais auxiliam do que realizam tarefas independentes.

    O que é mais lógico é a preocupação com a semântica da orientação a objetos.

  6. Pra testar privados uso o send, não sei se isso é uma boa prática.

    class Foo
      def coisa
        ‘nice’
      end
      private :coisa
    end
    
    Foo.new.send(:coisa).should be(’nice’)
    
  7. Método o que?????? uieaheiuaheiuahe
    Gostei muito do seu artigo.
    Essa preocupação com o tipo que será atribuido aos métodos é importantíssima.
    Parabéns pela iniciativa.

  8. Você poderia passar o link do artigo que você leu? Fiquei interessado em conhecer o ponto de vista do programador que disse isso….

  9. Carlos,

    Qual a melhor forma de ultrapassar o encapsulamento para testes?

  10. Gabriel,

    Aí é que está, eu não costumo ultrapassar. Simplesmente testo os métodos públicos e teoricamente meus métodos privados também estarão sendo testados.

Deixe um comentário