Vejo os benefícios de tornar objetos no meu programa imutáveis. Quando estou realmente pensando profundamente em um bom design para o meu aplicativo, muitas vezes chego naturalmente a muitos dos meus objetos sendo imutáveis. Muitas vezes chega ao ponto em que eu gostaria de ter todos os meus objetos imutáveis.
Esta pergunta lida com a mesma idéia, mas nenhuma resposta sugere qual é uma boa abordagem para a imutabilidade e quando realmente usá-la. Existem bons padrões de design imutáveis? A idéia geral parece ser "tornar objetos imutáveis, a menos que você precise absolutamente mudar", o que é inútil na prática.
Minha experiência é que a imutabilidade direciona meu código cada vez mais ao paradigma funcional e essa progressão sempre acontece:
- Começo a precisar de estruturas de dados persistentes (no sentido funcional), como listas, mapas etc.
- É extremamente inconveniente trabalhar com referências cruzadas (por exemplo, nó de árvore que faz referência a seus filhos enquanto filhos referenciam seus pais), o que me faz não usar referências cruzadas, o que novamente torna minhas estruturas de dados e código mais funcionais.
- A herança para para fazer algum sentido e eu começo a usar a composição.
- Todas as idéias básicas de OOP, como encapsulamento, começam a desmoronar e meus objetos começam a parecer funções.
Neste ponto, praticamente não estou mais usando nada do paradigma OOP e posso apenas mudar para uma linguagem puramente funcional. Assim, minha pergunta: existe uma abordagem consistente para o bom design imutável de OOP ou sempre é que, quando você leva a idéia imutável ao máximo de seu potencial, você sempre acaba programando em uma linguagem funcional que não precisa mais de nada do mundo da OOP? Existem boas diretrizes para decidir quais classes devem ser imutáveis e quais devem permanecer mutáveis para garantir que a POO não desmorone?
Apenas por conveniência, darei um exemplo. Vamos ter uma ChessBoard
coleção imutável de peças de xadrez imutáveis (estendendo a classe abstrataPiece
) Do ponto de vista do POO, uma peça é responsável por gerar movimentos válidos a partir de sua posição no tabuleiro. Mas, para gerar os movimentos, a peça precisa de uma referência ao seu tabuleiro, enquanto o tabuleiro precisa ter referência às suas peças. Bem, existem alguns truques para criar essas referências cruzadas imutáveis, dependendo da sua linguagem OOP, mas eles são difíceis de gerenciar, é melhor não ter uma peça para referenciar seu quadro. Mas então a peça não pode gerar seus movimentos, pois não sabe o estado do tabuleiro. Em seguida, a peça se torna apenas uma estrutura de dados contendo o tipo e a posição da peça. Você pode usar uma função polimórfica para gerar movimentos para todos os tipos de peças. Isso é perfeitamente possível na programação funcional, mas quase impossível no OOP sem verificações de tipo de tempo de execução e outras práticas ruins de OOP ...