Imutabilidade de objetos grandes

Estou pensando em fazer um sistema que aproveite ao máximos os objetos, que possa fazer um cache deles e reutilizá-los sem problemas.

Uma forma de ajudar a evitar problemas de performance, concorrência e distribuição dos objetos seria fazer com que os objetos sejam imutáveis, ou seja, caso algum dado neles mude, um novo objeto é criado, e o método que o alterou(ou pediu a alteração) recebe esse novo objeto, deixando a versão antiga dela intacta para o resto do sistema.

Bem, isso é fácil de fazer com os wrappers dos tipos de dados, Strings, BigIntegers, e com objetos-valor (Fowler). Entretanto, como faço isso para algo maior e mais complexo, como Pessoas, Produtos, que tem nomes, datas e uma série de dados extras dentro deles?

O problema que vejo é que a cada chamada que altera o objeto, ele retorna um novo objeto. E com múltiplas alterações em série, seriam dezenas de chamadas para criação de novos objetos… Por outro lado, ter um method chaining até que é bom para aumentar a legibilidade, mas é algo a ser estudado.

Uma das minhas maiores preocupações é uma maneira muito fácil de isso ser usado, sem ficar brigando com a linguagem nem escrevendo muito código.

Algum pensamento sobre assunto? Usar Factories, Builders, padrões de construção? Ou soluções mais simples para este reaproveitamento?

Um problema que eu vejo com a Imutabilidade de objetos grandes é que eles podem ser marcados como ‘inelegiveis’ à coleta de lixo.

Eu pensaria nisso com cuidado, de repente guardando SoftReferences ou WeakReferences dos mesmos.

Antes de responder com a minha sugestão eu gostaria de dizer que nem todo ‘objeto grande’ deve ser feito immutable. Especialmente alguns exemplos que vocë deu. Vou explicar:

Você pode distinguir os seus objetos entre Value Objects e Business Entities (tem outros tipos, lógico, mas pra essa discussão estes são os dois tipos importantes). Value Objects representam uma característica, como uma Cor, um Valor Monetário, etc. Business Entities representam, bem, entidades de negócio :stuck_out_tongue: A grande sacada é você se perguntar: Dados dois objetos da classe Xyz com os mesmos valores em seus atributos, eles podem ser intercambiáveis? Se forem dois objetos do tipo Cor, sim, isto é, dois objetos que não tem nada a ver com o outro podem apontar para o mesmo objeto representando o Amarelo. Por outro lado, dois objetos do tipo Pessoa com os mesmos valores nos seus atributos não são intercambiáveis. Você pode ter duas Pessoas com o mesmo nome e data de nascimento, mas cada instância representa uma pessoa diferente.

A idéia é que Value Objects podem (e em geral devem) ser feitos imutáveis, pra tirar proveito do compartilhamento dos objetos e evitar bugs bizarros quando alguém modifica um Value Object que é compartilhado por um outro cara. Imagina que dois caras apontam para o mesmo objeto do tipo Cor representando o Amarelo e um desses caras resolve mudar sua cor pra Vermelho. Aí o outro cara ia mudar pra vermelho também e ia ficar difícil de debugar e entender onde está o problema.

Business Entities não devem ser imutáveis, porquê você não ganha nada com isso. Sempre que dois caras estiverem referenciando o mesmo objeto Pessoa, eles estão realmente apontando para a mesma entidade, então qualquer mudança feita por um deve ser vista pelo outro.

Bem, tendo dito tudo isto, como fazer pra criar ou modificar objetos complexos que são imutáveis? Uma idéia é usar uma classe auxiliar que que é mutável, e usar ela enquanto o objeto estiver sendo modificado, e no final chama classeMutavel.getImutableObject() ou algo do tipo. É exatamente a idéia do StringBuffer e StringBuilder. Quando vocë quer criar uma String complexa, você instancia um StringBuffer, faz todas a modificações que precisa fazer em cima do StringBuffer, e só no fim você chama toString pra obter a String final. Você pode fazer o mesmo pros seus objetos imutáveis.

Espero ter ajudado!!!

[quote=domingos.neto]Antes de responder com a minha sugestão eu gostaria de dizer que nem todo ‘objeto grande’ deve ser feito immutable. Especialmente alguns exemplos que vocë deu. Vou explicar:

Você pode distinguir os seus objetos entre Value Objects e Business Entities (tem outros tipos, lógico, mas pra essa discussão estes são os dois tipos importantes). Value Objects representam uma característica, como uma Cor, um Valor Monetário, etc. Business Entities representam, bem, entidades de negócio :stuck_out_tongue: A grande sacada é você se perguntar: Dados dois objetos da classe Xyz com os mesmos valores em seus atributos, eles podem ser intercambiáveis? Se forem dois objetos do tipo Cor, sim, isto é, dois objetos que não tem nada a ver com o outro podem apontar para o mesmo objeto representando o Amarelo. Por outro lado, dois objetos do tipo Pessoa com os mesmos valores nos seus atributos não são intercambiáveis. Você pode ter duas Pessoas com o mesmo nome e data de nascimento, mas cada instância representa uma pessoa diferente.

A idéia é que Value Objects podem (e em geral devem) ser feitos imutáveis, pra tirar proveito do compartilhamento dos objetos e evitar bugs bizarros quando alguém modifica um Value Object que é compartilhado por um outro cara. Imagina que dois caras apontam para o mesmo objeto do tipo Cor representando o Amarelo e um desses caras resolve mudar sua cor pra Vermelho. Aí o outro cara ia mudar pra vermelho também e ia ficar difícil de debugar e entender onde está o problema.

Business Entities não devem ser imutáveis, porquê você não ganha nada com isso. Sempre que dois caras estiverem referenciando o mesmo objeto Pessoa, eles estão realmente apontando para a mesma entidade, então qualquer mudança feita por um deve ser vista pelo outro.

Bem, tendo dito tudo isto, como fazer pra criar ou modificar objetos complexos que são imutáveis? Uma idéia é usar uma classe auxiliar que que é mutável, e usar ela enquanto o objeto estiver sendo modificado, e no final chama classeMutavel.getImutableObject() ou algo do tipo. É exatamente a idéia do StringBuffer e StringBuilder. Quando vocë quer criar uma String complexa, você instancia um StringBuffer, faz todas a modificações que precisa fazer em cima do StringBuffer, e só no fim você chama toString pra obter a String final. Você pode fazer o mesmo pros seus objetos imutáveis.

Espero ter ajudado!!![/quote]

Concordo com tudo o que ele falou. Vou só pro lado prático da coisa.

Se você tem um objeto “grande” imutável, então na maioria das vezes (e é bom que seja assim) este objeto referencia objetos que são também imutáveis. Caso seja assim, uma mudança de uma propriedade deste objeto implica em um novo objeto. Porém, se algumas propriedades inalteradas são referencias para objetos imutáveis, você simplesmente faz com que os dois objetos mantenham a mesma cópia do objeto! Não precisa de “deep copy”, pois por ser imutável, caso o novo objeto precise alterar sua referência, o antigo não se altera.

Porque voce acha que imutabilidade vai ajudar na performance e distribuicao dos seus objetos?

Voce ficaria surpreso se te dissesse que nem tudo na vida é business software, porque sempre essa tendencia de que querer aplicar DDD em todos os problemas?

Voce ficaria surpreso se te dissesse que nem tudo na vida é business software, porque sempre essa tendencia de que querer aplicar DDD em todos os problemas?[/quote]

Bem, nosso amigo ali em cima falou de Pessoa e Produto, então imagino que no caso dele seja.

Você tem razão. Mas acho que uns 99% do software escrito no mundo é business software (estimativa chutômetro minha sem nenhum fundamento científico) então a gente acaba fazendo essa suposição implícita. Ainda mais num fórum de Java.

Voce ficaria surpreso se te dissesse que nem tudo na vida é business software, porque sempre essa tendencia de que querer aplicar DDD em todos os problemas?[/quote]

Bem, nosso amigo ali em cima falou de Pessoa e Produto, então imagino que no caso dele seja.

Você tem razão. Mas acho que uns 99% do software escrito no mundo é business software (estimativa chutômetro minha sem nenhum fundamento científico) então a gente acaba fazendo essa suposição implícita. Ainda mais num fórum de Java.[/quote]

A proposito: nada do que falei é conceito específico de Domain Driven Design.

Cache é o motivo principal, ou mais especificamente “não consultar o banco 10 vezes para obter o mesmo dado”. O objetivo era aproveitar o mesmo objeto, e não deixar que mudanças nele feitos em uma parte propaguem para outras, já que não tenho o controle pleno sobre esse emanharado de sistemas que se ligam.

Mas depois de ler o que o domingos.neto postou, tenho outros pensamentos sobre isso que ainda estou processando.

Sobre business software ou não Pessoa e Produto foram os exemplos mais simples que achei, a minha área mesmo são Notas Fiscais :), apesar que essas perguntas que estou fazendo são para um framework corporativo de cadastro de pessoas (e qualquer coisa relacionada a elas).

Cache é o motivo principal, ou mais especificamente “não consultar o banco 10 vezes para obter o mesmo dado”. O objetivo era aproveitar o mesmo objeto, e não deixar que mudanças nele feitos em uma parte propaguem para outras, já que não tenho o controle pleno sobre esse emanharado de sistemas que se ligam.

Mas depois de ler o que o domingos.neto postou, tenho outros pensamentos sobre isso que ainda estou processando.

Sobre business software ou não Pessoa e Produto foram os exemplos mais simples que achei, a minha área mesmo são Notas Fiscais :), apesar que essas perguntas que estou fazendo são para um framework corporativo de cadastro de pessoas (e qualquer coisa relacionada a elas).[/quote]

Me parece que voce esta tentando arrumar uma solucao generica para problemas localizados (e diferentes). Pela descricao inicial do problema parecia que voce queria fazer um intern pool pra cada um dos seus objetos, que seriam muitos, porque ao inves de objetos “grandes”, na verdade seriam objetos com muitas associacoes com outros objetos, e estes por sua vez deveria ser imutaveis tambem, ou seja, provavelmente nao tem porque todo esse trabalho.

Ou entao… faz um intern pool de List e bola pra frente!

Programadores Lisp programaram 50 anos usando apenas listas, voce por sua vez pode contar com String e Numbers, ja é um grande ajuda. :twisted: