20 mensagens, 11 participantes
Static, OO e classes *Utils
#design #oo #static #procedural
|
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 |
| Share | | |
|
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… 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. |
|
15 mensagens |
Olá André, Ao ver o seu exemplo da classe Utils, a primeira sensação que eu tive, foi de que 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. |
|
33 mensagens |
Eu ainda tenho esperança de ver extension methods em Java puro. |
|
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? |
|
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 Grande abraço!
--
http://njalldev.wordpress.com |
|
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. |
|
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. |
|
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
--
Guilherme Silveira |
|
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 Desenvolvedor de Sistemas |
|
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?
--
Guilherme Silveira |
|
18 mensagens |
Tranquilo Guilherme, vlw!
--
Renato M. da Gama Desenvolvedor de Sistemas |
|
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. |
|
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!
--
Guilherme Silveira |
|
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. |
|
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. |
|
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.
--
Guilherme Silveira |
|
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/ |
|
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? |
|
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?
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! |

