20 mensagens, 11 participantes

Static, OO e classes *Utils

#design #oo #static #procedural

 
Avatar Andre Brito
53 mensagens

Opa.

Tenho essa dúvida a muito tempo e nunca soube colocar ela da forma correta. Agora acho que chegou a hora, mesmo que eu não consiga me explicar da forma correta. “It’s an itch that needs to be scratched”, hehe.

Eu li um artigo a um tempo atrás no blog da Caelum que me fez refletir bastante, principalmente sobre métodos static. O artigo se encontra neste link: http://blog.caelum.com.br/singletons-e-static-p…

Para o escopo desse tópico, ignorem a parte de Singleton.

No final, o autor do artigo, Paulo Silveira, fala que ainda existem métodos que ele acha válido serem static, como todos os da classe Math. Quer dizer, métodos como sqrt e tan não precisam guardar estado, certo? Eu também não vejo razão pra deixar eles como métodos de instância.

Hoje eu estava vendo uma apresentação, acho que era do Guilherme Silveira (acho), onde falava que métodos static são ruins porque deixam o código cada vez mais procedural. Em partes, eu concordo com o apresentador (seja quem for). Quer dizer, você não está lidando com objeto, está lidando com uma funçãozinha que é feita em C ou Pascal da mesma forma. Mas e no caso de um método que elimina elementos repetidos numa lista, ou lê o conteúdo de um arquivo texto? Vou ter que escrever sempre o mesmo código dentro de cada objeto que precisar? E o DRY, como fica?

Onde trabalho e em sistemas feitos fora do trabalho, tenho classes *Utils. Classes do tipo FileUtils, ListUtils e SetUtils, que dão uma baita ajuda na hora que preciso de algo já pronto. Vi que muita gente usa isso também, como se fosse uma caixa de ferramentas. A própria Sun quando escreveu o código da Math (ou a Apache quando fez a Commons) pensou da mesma forma: pra que eu vou armazenar estado se é só, digamos, um serviço, que preciso? A Oracle também, dando continuidade ao Java criou a classe Objects, que faz algumas coisas desse tipo (vide aqui: http://download.oracle.com/javase/7/docs/api/ja… ).

Outra saída seria usar DI. Num FileUtils, por exemplo, enviar o path ou o objeto File do arquivo texto por parâmetro.

1 public class FileUtils { 2 3 public FileUtils(File theFile) { 4 // ... 5 } 6 7 public String getContentFromTheFile() { 8 // ... retorna o conteúdo do arquivo 9 } 10 11 }

Mas ainda, qual seria a diferença entre fazer isso acima ou fazer

1 public class FileUtils { 2 3 public static String getContentFromTheFile(File theFile) { 4 // ... retorna o conteúdo do arquivo 5 } 6 7 }

? Existe alguma relação com performance? Memória?

Em linguagens puramente OO (Python? Ruby? Scala?), teria esse static?

Isso vem me dando uma dorzinha de cabeça a um tempo e eu não sabia se perguntava ou se ficava com a minha opinião de que eu posso ter classes com métodos static (ou quem sabe, deixar a classe somente com método static). Mas sempre vejo o pessoal falando que isso não fica bem OO e tudo mais, e fico cada vez mais achando que estou acumulando cada vez mais o débito técnico por não me inteirar do assunto. Por isso fiz essa pergunta e, se pareci meio desesperado, me desculpem, não era e não é minha intenção.

Obrigado!

Editado: hoje também vi um trecho de código do RPonte (se você se sentir ofendido, por favor, me diga que retiro isso) em que também é usado static (uma StringUtils): https://gist.github.com/893494
Eu particularmente considero o RPonte uma pessoa que está inteirada do assunto e não faria nada de gambiarra e fita crepe e silvertape pros lados, o que me motivou a criar esse tópico pra discutir melhor com todos e ouvir diversas opiniões sobre o assunto.

Share |
 
Avatar Rubem Azenha
33 mensagens

Acho que em Java você não tem muito como fugir das classes Utils com métodos static.

Talvez você se sinta melhor em usar classes Utils com métodos static sabendo que o pessoal da Apache usa essa “pattern” em alguns dos seus projetos:

http://commons.apache.org/io/api-release/org/ap…
http://commons.apache.org/lang/api-release/org/…
http://commons.apache.org/lang/api-release/org/…

Aliás… uma coisa legal é conhecer o Apache Commons, muitas vezes você vai implementar um Utils que já existe nele.

Em Ruby até daria pra evitar isso fazendo um monkey patch nas classes.

 
Avatar Ronualdo Maciel
15 mensagens

Olá André,

Ao ver o seu exemplo da classe Utils, a primeira sensação que eu tive, foi de que
o comportamento getContentFromTheFile seria um comportamento da classe File.

Na minha opinião, o grande problema ao se criar uma classe como essa, utilizando métodos estáticos, é de que você corre o risco de estar separando a classe de seu comportamento.

Fiz uma pesquisa rápida aqui para garantir que eu não estava falando besteira, e acabei encontrando um artigo que talvez seja interessante para a discussão.

http://www.jroller.com/DhavalDalal/entry/kill_t…

 
Avatar Rubem Azenha
33 mensagens

Eu ainda tenho esperança de ver extension methods em Java puro.
Por enquanto a gente tem que se virar com os Utils mesmo…

 
Avatar Andre Brito
53 mensagens

Pois é pessoal, pelo visto as opiniões até agora é de que, em Java, não tem como fugir disso de forma elegante.

Seria muito interessante ter essa funcionalidade de extensão de métodos no Java, como acontece no Javascript com o protoype.

1 List.prototype.contains = function(element) { ... }

Em Java acho que ainda podemos dar uma escapada usando composição.

1 public class TextFile { 2 private File file; 3 4 public TextFile(String path) { 5 } 6 7 public TextFile(File file) { 8 } 9 10 public String getContentFromFile() { 11 // ... retorna o conteudo do texto 12 } 13 }

Mas nesse caso teríamos que abrir mão de polimorfismo (não nesse caso específico, que não vejo File sendo usada com polimorfismo, mas quando for Collections, pode acontecer).

O que vocês acham? É elegante ‘escapar’ dessa forma?

 
Avatar nj_all
51 mensagens

Exatamente,

Em outras linguagens (scala e ruby), temos classes abertas, o que nos permite extender os comportamentos das nossas classes, e assim nosso método fica aonde deveria estar (alta coesão), no caso dos métodos dá Math, será que não deveríamos ser capazes de fazer algo como
1 2.sqrt()?
isto é, este método deveria pertencer a classe integer (por exemplo).
Mas isto ocorre, por que nos temos os primitivos em java, ai alguém resolveu colocar em uma classe utilitária.
Ai caímos no que já disseram, provavelmente não temos muito como fugir de static em java.

Grande abraço!

 
Avatar Ronualdo Maciel
15 mensagens

Como regra geral, eu tento fugir de classes só com métodos static.

Acredito que na maioria das vezes, a utilização da composição como no exemplo que o André mostrou, resolve o problema. Outra solução válida que, na minha visão, pode ser considerada seria a utilização de herança, evitando dessa forma abrir mão das vantagens do polimorfismo.

 
Avatar jairo junior
1 mensagem

Além dos exemplos citados pelo Rubem de API’s com esse único propósito, poderia citar ainda o Google Guava (http://code.google.com/p/guava-libraries/), um projeto mais recente do que estes da Apache, mas que também trilha o mesmo caminho, classes com um conjunto de métodos estáticos para atender necessidades específicas.

Embora seja comum, a utilização de métodos estáticos para suprir uma deficiência da linguagem, acredito que estes devem ser utilizados apenas como um último recurso, quando for inviável a utilização de outra alternativa. Quando você não puder fazer como no exemplo do Narciso, vai cair no *Utils.

 
Avatar Guilherme Si...
Líder técnico
354 mensagens

Um método estático é um que tem acesso somente a variáveis globais. Muitos metodos estaticos que se invocam indica programação procedural: fuga total de controle de escopo. Não é um design bom (vide as vantagens de OO e de funcional).

Métodos estaticos que controlam acesso a variaveis mutaveis é o caso ruim. “Variaveis imutaveis” não tem problema (o método é reentrante?). Pois invoca-lo nao causa efeito colateral que a segunda invocacao causara loucuras. Ou ainda o que foi devolvido nao afetara o resultado de futuras invocações (quebra de encapsulamento).

Por isso metodos estaticos para fabricar dados, tudo bem, desde que não altere estado. Exemplo desses metodos? Que nao usam a palavra static mas é util? QUando chamamos new em um construtor educado. Não tem problema.

O Guava, o Hamcrest etc, são inteligentes nesse sentido: não há estado mutável. Essa é a diferença do static zoado (com variáveis globais mutaveis, conhecido como programação procedural de 1990) x static controlado (possivelmente funcional).

Não sugiro o estático mutável. Sugiro OO ou funcional para controlar escopo e modelar a interação das partes.

Abraço

 
Avatar Renato Gama
18 mensagens

Eu, depois que estou descobrindo a INJECAO DE DEPENDENCIA, passei a pensar assim; se um objeto so tem metodos static, e/ou nao guarda estado, posso usalo como metodos de classe porem como um singleton, e melhor ainda é que se eu deixar o container gerenciar o lifecicle, depois eu posso me dar a liberdade de resolver por exemplo guardar estado nesse objeto e apenas alterar uma configuracao no .xml! Me parece uma abordagem melhor, o que acham???

@Guiolherme o que e um metodo “reentrante” ???

--

Renato M. da Gama
twitter: @renatoargh
http://blog.renatogama.com

Desenvolvedor de Sistemas
Pedreira Rio Verde LTDA


profile for Renato Gama at Stack Overflow, Q&A for professional and enthusiast programmers

 
Avatar Guilherme Si...
Líder técnico
354 mensagens

Oi Renato,

É como um construtor. Você não precisa de um objeto para construi-lo (ou ele é um metodo do objeto que representa a classe, em algumas linguagens). Mas se ele não altera variaveis globais, você pode chama-lo quantas vezes quiser, sem uma vez quebrar a outra. Não existe estado compartilhado entre o que é devolvido pela invocacao do metodo duas vezes.

Se o metodo retorna um objeto que alterará uma variavel “global” (ou um singleton) você comeca a correr risco: esta compartilhando memoria entre dois acessos e vai incorrer em todos os problemas de encapsulamento.

Melhor com 2 exemplos?

 
Avatar Renato Gama
18 mensagens

Tranquilo Guilherme, vlw!

--

Renato M. da Gama
twitter: @renatoargh
http://blog.renatogama.com

Desenvolvedor de Sistemas
Pedreira Rio Verde LTDA


profile for Renato Gama at Stack Overflow, Q&A for professional and enthusiast programmers

 
Avatar vinicius qua...
2 mensagens

Na pratica o c# tbm possui classes que podem ser abertas e ter métodos adicionados como métodos de extensão.

Eu não sei se sou muito fã deste tipo de coisa pois eu acho que acaba violando demais o contrato de uma classe e adicionando métodos que acabam sendo interessantes em apenas alguns contextos e não para objetos da classe de forma geral.

 
Avatar Guilherme Si...
Líder técnico
354 mensagens

Quaiato, os extensions mthods tem acesso a variaveis membro? Em scala os implicits não tem, é composição, não abertura de classe.

Abraço!

 
Avatar vinicius qua...
2 mensagens

Não. Você terá acesso às propriedades do objeto que você recebe no contexto.

Extension method são métodos estáticos que recebem o objeto como argumento, parecido com os métodos em python que recebem self.
Você acessa qualquer coisa que seja público no objeto.

 
Avatar Rafael Ponte
3 mensagens

Olá,

Assim como alguns aqui comentaram não há muito para onde correr em Java, principalmente quando se trata dos bons e velhos Utils. Eles de fato, quando utilizados sem exageros, quebram um galho danado!

Mas como o Guilherme ressaltou, se não há alteração/acesso de variáveis mutáveis ou efeitos colaterais na chamada do método então não vejo o static como algo ruim, e é isso que ocorre com todos as classes Utils que tenho visto.

Enfim, normalmente crio métodos static para classes que não alteram estado e factory methods, por exemplo.

 
Avatar Guilherme Si...
Líder técnico
354 mensagens

Vinicius, perfeito… se não quebra encapsulamento, sem problemas.

Rafael, perfeito tambem. Se não tem variaveis em escopo, é uma função solta. Static nela. EM outro mundo ela seria uma funcao solta tambem.
Creio que a unica maneira de iniciar a instanciacao de um objeto é atraves de uma funcao solta, então seja um new, ou um outro método, ok tambem. Melhor ainda se brincar com DI.

 
Avatar Celso Martins
26 mensagens

Eu realmente não gosto nem um pouco de métodos utilitários. Para resolver o problema de leitura e escrita de arquivos, comentado aqui, encapsulei esta complexidade em um FileIn e FileOut e criei um file.jar que uso em meus projetos quando preciso deste tipo de serviço. Assim, instancio, passando as dependencias pelo construtor e chamo o método de leitura, por exemplo. Segue a classe FileIn:

1 public class FileIn { 2 3 private String fullPath = ""; 4 private String fullName = ""; 5 6 public FileIn(String fullPath, String fullName) throws MalformedPathException { 7 super(); 8 if (!fullPath.endsWith("\\")){ 9 throw new MalformedPathException("O caminho deve terminar com \"\\\" ----> path: " + fullPath); 10 } 11 this.fullPath = fullPath; 12 this.fullName = fullName; 13 } 14 15 public List<String> read() throws IOException, EmptyFileException{ 16 BufferedReader reader = new BufferedReader(new FileReader(new File(getFileName()))); 17 18 String line = ""; 19 20 List<String> linhas = new ArrayList<String>(); 21 while ((line = reader.readLine()) != null){ 22 linhas.add(line); 23 } 24 25 if (linhas.isEmpty()){ 26 throw new EmptyFileException("O arquivo está vazio"); 27 } 28 29 return linhas; 30 } 31 32 private String getFileName(){ 33 return fullPath + fullName; 34 } 35 }
--

Desenvolvimento psicopata – Qualidade Total → http://celsoavmartins.blogspot.com/
Twitter → @celsoavmartins

 
Avatar Andre Brito
53 mensagens

Pessoal, muito obrigado. Essa discussão clareou bastante as minhas ideias. Concluí que posso usar static, desde que não altere o estado delas.

Pelo visto, é necessário tomar muito cuidado com isso. Quando usar objetos imutáveis, como String, Integer e Long, sem problemas, posso colocar static porque nunca vou alterar e poder correr o risco de chegar em outro ponto da execução e o valor ser diferente por causa do método static, certo? E quando usar objetos mutáveis, tomar bastante cuidado pra não alterar o estado dele dentro do método.

É mais ou menos isso?

 
Avatar Alexandre Aq...
22 mensagens

Concordo com o Renato Gama. Porque não usar um framework de injeção de dependências para controlar o ciclo de vida das classes utilitárias?

Se ele é Singleton, tem um Pool, ou tem várias instâncias fica separado do código do objeto. Na maioria das vezes, para essas classes, você vai usar o framework de DI para deixar apenas uma instância (Singleton).

Na prática, tenho observado que a testabilidade de objetos que dependem dos métodos estáticos do Apache Commons, fica prejudicada. Principalmente quando a lógica do objeto que está sendo testado dependem de estímulos das classes utilitárias.

Por exemplo: a minha classe depende de FileUtils e eu preciso garantir como a minha classe reage a uma exceção em algum método de FileUtils.

Os métodos de FileUtils são estáticos. Então, quais são as minhas alternativas?

  • Uma outra alternativa que pode ser usada quando possível é realmente executar o método, deixando a exceção real acontecer. Acho que essa maneira é preferível. Para FileNotFoundException, por exemplo, é tranquilo. Mas para algumas outras exceções (sei lá, SecurityException) é mais complicado.

Mas e se os métodos de FileUtils não fossem estáticos? O código do Celso Martins ficou interessante.

Talvez devessemos fazer um fork do Apache Commons ou Guava para tornar os métodos não estáticos!

Formatação

Faça o login

CADASTRE-SE AGORA

. .