Vindo de um background OOP (Java), estou aprendendo Scala sozinho. Embora eu possa ver facilmente as vantagens de usar objetos imutáveis individualmente, estou tendo dificuldades para ver como é possível projetar um aplicativo inteiro como esse. Vou dar um exemplo:
Digamos que eu tenha objetos que representem "materiais" e suas propriedades (estou criando um jogo, então realmente tenho esse problema), como água e gelo. Eu teria um "gerente" que possui todas essas instâncias de materiais. Uma propriedade seria o ponto de congelamento e derretimento e o material para o qual congela ou derrete.
[EDIT] Todas as instâncias materiais são "singleton", como um Java Enum.
Quero que "água" diga que congela para "gelo" a 0 ° C e "gelo" para dizer que derrete para "água" a 1 ° C. Mas se água e gelo são imutáveis, eles não podem obter uma referência um para o outro como parâmetros construtores, porque um deles precisa ser criado primeiro e que não foi possível obter uma referência para o outro ainda não existente como parâmetro construtor. Eu poderia resolver isso fornecendo a ambos uma referência ao gerente, para que eles pudessem consultá-lo para encontrar a outra instância material de que precisam toda vez que forem solicitados por suas propriedades de congelamento / derretimento, mas então eu tenho o mesmo problema entre o gerente e os materiais, que eles precisam de uma referência um para o outro, mas só podem ser fornecidos no construtor para um deles, para que o gerente ou o material não possa ser imutável.
Não há maneira de contornar esse problema, ou preciso usar técnicas de programação "funcionais" ou algum outro padrão para resolvê-lo?
fonte
h2o
materiaisRespostas:
A solução é trapacear um pouco. Especificamente:
Crie A, mas deixe sua referência a B não inicializada (como B ainda não existe).
Crie B e faça com que aponte para A.
Atualize A para apontar para B. Não atualize A ou B depois disso.
Isso pode ser feito explicitamente (exemplo em C ++):
ou implicitamente (exemplo em Haskell):
O exemplo de Haskell usa avaliação lenta para obter a ilusão de valores imutáveis mutuamente dependentes. Os valores começam como:
a
eb
ambos são válidos para a cabeça normal, independentemente. Cada contras pode ser construído sem precisar do valor final da outra variável. Quando a conversão é avaliada, ela apontará para os mesmosb
pontos de dados para.Portanto, se você deseja que dois valores imutáveis apontem um para o outro, é necessário atualizar o primeiro após a construção do segundo ou usar um mecanismo de nível superior para fazer o mesmo.
No seu exemplo particular, eu posso expressá-lo em Haskell como:
No entanto, estou deixando de lado a questão. Eu imagino que, em uma abordagem orientada a objetos, em que um
setTemperature
método seja anexado ao resultado de cadaMaterial
construtor, você precisará que os construtores apontem um para o outro. Se os construtores forem tratados como valores imutáveis, você poderá usar a abordagem descrita acima.fonte
No seu exemplo, você está aplicando uma transformação a um objeto, para que eu usasse algo como um
ApplyTransform()
método que retorne umBlockBase
ao invés de tentar alterar o objeto atual.Por exemplo, para alterar um IceBlock para WaterBlock aplicando um pouco de calor, eu chamaria algo como
e o
IceBlock.ApplyTemperature()
método ficaria assim:fonte
BlockList.WaterBlock
vez de criar um novo bloco?BlockList
é apenas umastatic
classe que é responsável pelas únicas instâncias de cada bloco, para que você não precisa criar uma instância deBlockList
(eu estou acostumado a C #)Outra maneira de interromper o ciclo é separar as preocupações de material e transmutação, em alguma linguagem inventada:
fonte
Se você usar uma linguagem funcional e quiser obter os benefícios da imutabilidade, deverá abordar o problema com isso em mente. Você está tentando definir um tipo de objeto "gelo" ou "água" que possa suportar uma variedade de temperaturas - para oferecer suporte à imutabilidade, você precisará criar um novo objeto sempre que a temperatura mudar, o que é um desperdício. Portanto, tente tornar os conceitos de tipo de bloco e temperatura mais independentes. Eu não conheço Scala (está na minha lista de aprendizado :-)), mas emprestando a resposta de Joey Adams em Haskell , sugiro algo como:
ou talvez:
(Nota: eu não tentei executar isso, e meu Haskell está um pouco enferrujado.) Agora, a lógica de transição é separada do tipo de material, para que não desperdice tanta memória e (na minha opinião) é bastante um pouco mais funcionalmente orientado.
fonte