Adoro o "padrão" imutável por causa de seus pontos fortes e, no passado, achei benéfico projetar sistemas com tipos de dados imutáveis (alguns, a maioria ou até todos). Frequentemente, quando faço isso, me pego escrevendo menos bugs e a depuração é muito mais fácil.
No entanto, meus colegas em geral evitam o imutável. Eles não são inexperientes (longe disso), mas escrevem objetos de dados da maneira clássica - membros privados com um getter e um setter para cada membro. Então, geralmente seus construtores não aceitam argumentos, ou talvez apenas tomem alguns argumentos por conveniência. Com frequência, a criação de um objeto se parece com isso:
Foo a = new Foo();
a.setProperty1("asdf");
a.setProperty2("bcde");
Talvez eles façam isso em todos os lugares. Talvez eles nem definam um construtor que use essas duas strings, por mais importantes que sejam. E talvez eles não alterem o valor dessas strings mais tarde e nunca precisem. Claramente, se essas coisas são verdadeiras, o objeto seria melhor projetado como imutável, certo? (o construtor pega as duas propriedades, sem setters).
Como você decide se um tipo de objeto deve ser projetado como imutável? Existe um bom conjunto de critérios para julgá-lo?
Atualmente, estou debatendo se é necessário mudar alguns tipos de dados em meu próprio projeto para imutáveis, mas precisaria justificá-los para meus colegas, e os dados nos tipos podem (MUITO raramente) mudar - e nesse momento é claro que você pode alterar da maneira imutável (crie uma nova, copiando as propriedades do objeto antigo, exceto as que você deseja alterar). Mas não tenho certeza se esse é apenas o meu amor por imutáveis aparecendo, ou se há uma necessidade real / benefício deles.
fonte
Respostas:
Parece que você está se aproximando de trás para a frente. Deve-se usar como padrão imutável. Apenas torne um objeto mutável se você precisar / simplesmente não puder fazê-lo funcionar como um objeto imutável.
fonte
O principal benefício de objetos imutáveis é garantir a segurança da linha. Em um mundo em que múltiplos núcleos e threads são a norma, esse benefício se tornou muito importante.
Mas usar objetos mutáveis é muito conveniente. Eles têm um bom desempenho e, desde que você não os modifique a partir de threads separados, e você tenha um bom entendimento do que está fazendo, eles são bastante confiáveis.
fonte
Existem duas maneiras principais de decidir se um objeto é imutável.
a) Com base na natureza do objeto
É fácil capturar essas situações porque sabemos que esses objetos não serão alterados depois que forem construídos. Por exemplo, se você tem uma
RequestHistory
entidade e, por natureza, as entidades não mudam depois que ela é construída. Esses objetos podem ser projetados diretamente como classes imutáveis. Lembre-se de que o objeto de solicitação é mutável, pois pode alterar seu estado e a quem é atribuído etc., ao longo do tempo, mas o histórico de solicitações não muda. Por exemplo, havia um elemento de histórico criado na semana passada quando passou do estado enviado para o estado atribuído E essa entidade do histórico nunca pode ser alterada. Portanto, este é um caso imutável clássico.b) Com base na escolha do projeto, fatores externos
Isso é semelhante ao exemplo java.lang.String. As strings podem realmente mudar ao longo do tempo, mas, por design, elas o tornaram imutável devido a fatores de cache / pool de strings / simultaneidade. Da mesma forma, o cache / simultaneidade, etc, pode desempenhar um bom papel ao tornar um objeto imutável se o cache / simultaneidade e o desempenho relacionado forem vitais no aplicativo. Mas essa decisão deve ser tomada com muito cuidado após analisar todos os impactos.
A principal vantagem dos objetos imutáveis é que eles não são submetidos ao padrão de queda de ervas daninhas. Se o objeto não sofrer nenhuma alteração ao longo da vida útil, isso facilita muito a codificação e a manutenção.
fonte
Minimizar o estado de um programa é altamente benéfico.
Pergunte a eles se eles desejam usar um tipo de valor mutável em uma de suas classes para armazenamento temporário de uma classe cliente.
Se eles disserem sim, pergunte por quê? O estado mutável não pertence a um cenário como este. Forçá-los a criar o estado ao qual realmente pertence e tornar o estado dos seus tipos de dados o mais explícito possível são excelentes motivos.
fonte
A resposta é um pouco dependente do idioma. Seu código se parece com Java, onde esse problema é o mais difícil possível. Em Java, os objetos podem ser passados apenas por referência e o clone é completamente quebrado.
Não existe uma resposta simples, mas com certeza você deseja tornar objetos de pequeno valor imutável. O Java tornou as Strings corretamente imutáveis, mas a Data e o Calendário incorretamente tornaram mutáveis.
Portanto, torne imutáveis os objetos de pequeno valor e implemente um construtor de cópias. Esqueça tudo sobre o Cloneable, ele é tão mal projetado que é inútil.
Para objetos de maior valor, se for inconveniente torná-los imutáveis, facilite a cópia.
fonte
Um tanto contra-intuitivo, nunca precisar mudar as cordas mais tarde é um argumento muito bom de que não importa se os objetos são imutáveis ou não. O programador já está tratando-os como efetivamente imutáveis, independentemente de o compilador aplicá-lo ou não.
A imutabilidade não costuma doer, mas nem sempre ajuda. A maneira mais fácil de saber se seu objeto pode se beneficiar da imutabilidade é se você precisar fazer uma cópia do objeto ou adquirir um mutex antes de alterá-lo . Se isso nunca muda, a imutabilidade realmente não compra nada para você e, às vezes, torna as coisas mais complicadas.
Você faz ter um bom ponto sobre o risco de construir um objeto em um estado inválido, mas isso é realmente uma questão separada da imutabilidade. Um objeto pode ser mutável e sempre em um estado válido após a construção.
A exceção a essa regra é que, como o Java não suporta parâmetros nomeados nem parâmetros padrão, às vezes pode ser difícil projetar uma classe que garanta um objeto válido usando apenas construtores sobrecarregados. Não muito com o caso de duas propriedades, mas também há algo a ser dito sobre consistência, se esse padrão for frequente com classes semelhantes, mas maiores, em outras partes do seu código.
fonte
Object
recursos ocultos "; alterar um objeto diretamente não seria semanticamente diferente de substituir a referência por uma referência a um novo objeto que fosse idêntico, exceto pela alteração indicada. Uma das maiores fraquezas semânticas em Java, IMHO, é que ele não possui meios pelos quais o código possa indicar que uma variável deve ser apenas a única referência não efêmera a algo; as referências devem ser passáveis apenas para métodos que não podem manter uma cópia após o retorno.Eu posso ter uma visão de nível excessivamente baixo disso e provavelmente porque estou usando C e C ++, que não tornam exatamente tão simples tornar tudo imutável, mas vejo os tipos de dados imutáveis como um detalhe de otimização para escrever mais funções eficientes desprovidas de efeitos colaterais e podem fornecer recursos com muita facilidade, como desfazer sistemas e edição não destrutiva.
Por exemplo, isso pode ser extremamente caro:
... se
Mesh
não foi projetado para ser uma estrutura de dados persistente e foi, em vez disso, um tipo de dados que exigia copiá-lo completamente (o que poderia abranger gigabytes em algum cenário), mesmo que tudo o que faremos seja alterar um parte dela (como no cenário acima, onde apenas modificamos as posições dos vértices).É nesse momento que procuro a imutabilidade e projeto a estrutura de dados para permitir que partes não modificadas sejam copiadas e contadas de referência superficialmente, para permitir que a função acima seja razoavelmente eficiente sem ter que copiar em profundidade malhas inteiras enquanto ainda é capaz de escrever o função livre de efeitos colaterais, o que simplifica drasticamente a segurança da rosca, a segurança da exceção, a capacidade de desfazer a operação, aplicá-la de maneira não destrutiva etc.
No meu caso, é muito caro (pelo menos do ponto de vista da produtividade) tornar tudo imutável, então eu o guardo para as classes que são caras demais para serem copiadas na íntegra. Essas classes geralmente são estruturas de dados pesadas, como malhas e imagens, e eu geralmente uso uma interface mutável para expressar alterações a elas através de um objeto "construtor" para obter uma nova cópia imutável. E não estou fazendo tanto para tentar obter garantias imutáveis no nível central da classe, mas para me ajudar a usar a classe em funções que podem estar livres de efeitos colaterais. Meu desejo de tornar
Mesh
imutável acima não é diretamente tornar imutáveis as malhas, mas permitir facilmente escrever funções livres de efeitos colaterais que introduzem uma malha e produzem uma nova sem pagar uma enorme memória e custo computacional em troca.Como resultado, tenho apenas quatro tipos de dados imutáveis em toda a minha base de código, e são todas pesadas estruturas de dados, mas eu as uso intensamente para me ajudar a escrever funções livres de efeitos colaterais. Essa resposta pode ser aplicável se, como eu, você estiver trabalhando em um idioma que não torne tão fácil tornar tudo imutável. Nesse caso, você pode mudar seu foco para evitar que a maior parte de suas funções evite efeitos colaterais; nesse momento, convém tornar imutáveis estruturas de dados selecionadas, tipos PDS, como um detalhe de otimização para evitar cópias caras e caras. Enquanto isso, quando eu tenho uma função como esta:
... então não tenho tentação de tornar vetores imutáveis, pois são baratos o suficiente para apenas copiar na íntegra. Não há nenhuma vantagem de desempenho a ser obtida aqui, transformando Vectors em estruturas imutáveis que podem copiar partes não modificadas de baixa profundidade. Tais custos superariam o custo de apenas copiar todo o vetor.
fonte
Se o Foo tiver apenas duas propriedades, será fácil escrever um construtor que aceite dois argumentos. Mas suponha que você adicione outra propriedade. Então você precisa adicionar outro construtor:
Neste ponto, ainda é gerenciável. Mas e se houver 10 propriedades? Você não quer um construtor com 10 argumentos. Você pode usar o padrão do construtor para construir instâncias, mas isso adiciona complexidade. A essa altura, você estará pensando "por que não adicionei setters para todas as propriedades como as pessoas normais fazem"?
fonte