Objetos Imutáveis, qual a vantagem?

Pessoal tava dando uma estudada aqui no Padrão Builder ideal para a criação de objetos imutáveis, mas fiquei pensando numa serventia pra esse tipo de objeto.
Poderiam me dar uma força?

PS -> dei uma lida nesse artigo http://www.ibm.com/developerworks/br/library/j-ft4/ e ME PARECEU ser algo relacionado à desempenho, mas não ficou claro.

Vou dar uma motivação:

Pense na classe String, ela guarda um array de chars…

class String { char[] contents; }

A classe String é imutável, isso é, seu estado não pode ser modificado externamente. Isso significa que o conteúdo de contents nunca será alterado.

Isso te garante que quando voce passar sua String como parametro para um método, seu conteúdo não sofrerá alterações após a chamada, exemplo:

String x = ...; metodoQualquerDesconhecido(x); //nesse local o valor de x permanece inalterado

Um exemplo, vamos montar uma String que concatene os números de 0 até 100:

StringBuilder builder = new StringBuilder(); builder.append("Numeros: 1"); for (int i = 2; i <=100; ++i) builder.append(", ").append(i); builder.append("."); System.out.println(builder.toString());

Nesse caso o Builder:
-Evitou que Strings fossem copiadas repetidas vezes (cada concatenação geraria uma nova String);
-Evitou que eu tivesse que fazer casts (o builder nesse caso tem métodos para tipos inteiros, float, double, etc);

De 0 até 100 ? Não seria de 1 até 100? ( Sim, estou sem nada para fazer, hahaha :stuck_out_tongue: )

Além do que os colegas já explicaram, também é uma boa utilizar objetos imutáveis quando você trabalha com programação concorrente (quando possível, é claro). Um dos problemas deste tipo de programação é justamente o fato de o estado de um objeto ser alterado de forma concorrente e por isso apresentar estados inconsistentes entre diferentes threads caso não seja utilizado um mecanismo que coordene esta mudança de estado que é feita de forma concorrente. Já se você utilizar objetos imutáveis não existe essa preocupação porque o estado do objeto não será modificado após o mesmo ser criado e, desta forma, não é necessário utilizar um mecanismo de coordenação para acessar este estado interno do objeto, já que o estado não será modificado.

simplesmente a melhor resposta… pois estava em busca de uma resposta que não fosse tão óbvia como o exemplo da classe String.

Algo q eu descobri recentemente é que por Strings serem imutáveis e existir o pool de Strings elas são altamente eficientes para uso em HashMap

Quando vc faz:

map.get(“chave”);

“chave” é uma constante e sempre vai se referir ao mesmo objeto. A classe String tem um atributo hashcode q só é calculado 1 vez pra cada instância

Além dessa otimização de só calcular o hashcode uma vez por execução do programa inteiro, na hora pegar o valor também é otimizado, pq o normal é o HashMap dar um equals no valor encontrado para garantir que é o mesmo que você passou na chave, mas quando é a mesma instância (oq acontece por causa do pool) ele nem chega a chamar o equals, pq dá certo logo no == (e equals de String é meio pesado…)

Se String não fosse imutável esse esquema todo ia quebrar, pq vc poderia alterar o array de char e ai o hashcode ia se tornar inválido. Não faz sentido pool sem objetos imutáveis

Ah… é que você colocou uma coisa no título do seu tópico, mas no texto perguntou sobre o padrão Builder.

Acho que um dos melhores artigos sobre objetos serem ou não imutáveis é esse aqui, do Brian Goetz:

Em resumo, boas razões para um objeto ser imutável:

  • Seguro em multi-threading;
  • Podem ser usados com segurança como chaves de hashmaps e sets;
  • Podem ser compartilhados entre múltiplos objetos (padrão Flyweight);
  • Resistentes a código mau escrito.

Uma desvantagem de objetos imutáveis é que eles não podem ser reutilizados num pool de objetos por exemplo.

Tirando isso tem só vantagem para segurança e thread-safe.

Uma das vantagens dele é justamente poderem ser cacheados em pools e reutilizados, como o Java faz com Strings e wrappers de tipo primitivo, e como é feito no padrão Flyweight.

Então, acho que não entendi direito o que você quis dizer. Pode explicar melhor?

Uma das vantagens dele é justamente poderem ser cacheados em pools e reutilizados, como o Java faz com Strings e wrappers de tipo primitivo, e como é feito no padrão Flyweight.

Então, acho que não entendi direito o que você quis dizer. Pode explicar melhor?[/quote]

Imagina que vc tem um sistema que vai rodar durante 24 horas e produzir um bilhão de Strings diferentes. Cachear tudo isso é impraticável pois vai faltar memória. Criar um bilhão de Strings e soltá-las para o GC vai sobrecarregá-lo. Então como vc faz? Cria 100 mil StringBuilders (versão mutável de uma String), coloca num pool e reutiliza elas, ou seja, uma mesma instancia vai processar várias strings diferentes.

Se o GC não é problema, então vc pode simplesmente libera para o GC através de um cache que faz eviction, ou seja, vc utiliza String imutáveis e tudo bem.

Se o sistema é real-time, então vc utiliza esse esquema de pool com objetos mutáveis.

O wrapper Integer possui um cache interno para o método valueOf, mas só armazena valores de inteiros pequenos. Não dá para cachear 1 bilhão de inteiros diferentes.

Para entender melhor isso leia meu artigo sobre programação real-time em Java => http://mentablog.soliveirajr.com/2012/11/real-time-java-programming-without-gc/

Entendi. É que a sua frase ficou um tanto ambígua.

Tem razão. Objetos imutáveis podem ser cacheados. O que não podem é ser reutilizados para mais de um objeto diferente num pool de instancias.

Uma coisa é um CACHE e outra é um POOL de instancias.

Engraçado você citar object pooling e performance, pois o Brian Goetz advoga explicitamente contra essa técnica:

Segundo ele, o GC, por lidar com gerações de objetos, torna isso obsoleto.
O custo de uma exclusão de um objeto de vida curta no GC, segundo ele, é 0.

[quote=ViniGodoy]Engraçado você citar object pooling e performance, pois o Brian Goetz advoga explicitamente contra essa técnica:

Segundo ele, o GC, por lidar com gerações de objetos, torna isso obsoleto.
O custo de uma exclusão de um objeto de vida curta no GC, segundo ele, é 0.[/quote]

Tem que ver isso com calma. Vai depender do GC que vc está usando né? Tem o problema da compactação e tem o problema do stop-the-world.

AGAIN: isso tudo é irrelevante para todos os sistemas com exceção dos sistemas real-time que não podem ser interrompidos pelo GC.

Custo zero para remoção de um objeto, mesmo do EDEN me parece errado. É baixo mas não é zero. E o que vai acontecer é que o GC não vai entrar imediatamente para remover o objeto na hora que ele fica derefenced. Vai deixar acumular um pouco e depois limpar tudo quando o EDEN ficar cheio (again, depending on the GC you are using).

De qualquer forma, o argumento é de que esse custo ainda será menor do que o de gerenciar o Object Pool.

Estou questionando pq tipicamente trabalho com aplicações de tempo real em Java. E era isso que eu observava quando trabalhava com elas (o GC era geralmente extremamente veloz, muito mais do que nossas otimizações tentavam ser. Até porque ele roda num grau de privilégio bem maior do que os bytecodes).

Mas claro, eu também usava a VM Oracle, que é extremamente otimizada. Talvez a técnica ainda seja válida nas VMs da Google ou em outras VMs por aí.

Discordo. O custo do GC é alto se comparado a o custo de um Object Pool. Não estou nem levando em consideração CPU cache que vai torná-lo ainda mais rápido. O GC é veloz mas introduz uma latência aleatória e todos, com exceção talvez do recente G1GC que não sei em que pé está, fazem stop-the-world, logo não há como vc paralelizar isso, autmentar a prioridade, etc.

Agora um ponto importante: sistemas real-time devem ser lock-free. NIO com single-threaded é a solução recomendável, logo o seu pool não terá qualquer contenção de locks. Se vc quiser fazer um pool sincronizado para ser compartilhado por diversos threads aí vai ficar um lixo mesmo.

Em algum momento, algum estado terá que ser modificado, ou seu programa não faz nada.

simplesmente a melhor resposta… pois estava em busca de uma resposta que não fosse tão óbvia como o exemplo da classe String.[/quote]

Objetos imutáveis são um primeiro passo, mas tem pouco a ver com tornar programação concorrente possível.

Linguagens como Erlang, Scala e Clojure, não são consideradas apropriadas para programação concorrente porque seus objetos são imutáveis. Na verdade, você pode “modificar” um objeto nessas linguagens (senão não daria pra usa-las pra nada de útil), a diferença é que elas oferecem mecanismos para que isso seja feito com segurança, seja na forma de atores ou memória transacional.

Sem isso, mas apenas objetos imutáveis (Java, por exemplo), basicamente temos algo simples que não serve pra nada além de soluções específicas, como caches, pools, e afins.

[quote=faeldix]Pessoal tava dando uma estudada aqui no Padrão Builder ideal para a criação de objetos imutáveis, mas fiquei pensando numa serventia pra esse tipo de objeto.
Poderiam me dar uma força?
[/quote]

O conceito mais importante que é preciso entender para entender objetos imutáveis é que a imutabilidade não é algo que você acrescenta ao objeto, é algo que você retira.
Ou seja, por padrão todos os objetos deveriam ser imutáveis. Porquê ? Porque no mundo real eles são imutáveis e OO é uma abstração do mundo real. Por causa disso é mais simples entender e trabalhar com esses objetos. Veja como é simples trabalhar com string e integer porque eles são imutáveis. Veja como o fato de Date não ser um objeto imutável levanta tantos problemas que os modificadores foram todos deprecated. Valores são imutáveis, variáveis (como o nome indica) é que são mutáveis.
Se vc faz a = 2 vc quer que a continue sendo 2 para sempre. Não é agradável que o dois vire 3 porque o objeto decidiu sofrer uma metamorfose. Apenas atribuir o 3 à variável é que significa que vc quer que mude.

Então, os objetos são por padrão imutáveis.

Mas ai veio uma coisa chamada propriedade. Este conceito é de que o objeto tem propriedades que podem ser modificadas e com isso alterar o comportamento do objeto. Isto quase raramene é usado da forma certa e o uso mais comum é o padrão PropertyBag que em java toma a forma dos famosos beans. A ideia é que vc tem um objeto apenas com propriedades e ai vc mode mdificar cada uma. Ora, isto é usado por conveniencia. Em tese estes objetos podeiram ser imutáveis também. Vc seta os valores no construtor e/ou tem métodos especiais para alterar cada propriedade gerando um objeto novo a cada alteração. Isto seria a forma OO de fazer, mas ten vários problemas práticos sendo que o maior é que ninguem iria entender isto e seria muito burucrático. Então o get/set é mais simples e neste caso não produz efeitos secundários mas apenas se a modificação do objeto é bem controlada (transação)

Então, o builder é importante para muitas coisas e com certeza criar objetos imutáveis não é a principal, mas é possivel. A vantagem do builder é se o objeto a ser criado é muito complexo, ser imutável ou não é irrelevante à criação porque a mudança só pode acontecer depois que o objeto é criado.