Um ponto importante ainda não mencionado é que ter o estado de um objeto mutável torna possível a identidade do objeto que encapsula esse estado imutável.
Muitos programas são projetados para modelar coisas do mundo real que são inerentemente mutáveis. Suponha que, às 12h51, alguma variável AllTrucks
contenha uma referência ao objeto nº 451, que é a raiz de uma estrutura de dados que indica qual carga está contida em todos os caminhões de uma frota naquele momento (12h51) e alguma variável BobsTruck
pode ser usado para obter uma referência ao objeto nº 24601 e apontar para um objeto que indica qual carga está contida no caminhão de Bob naquele momento (12h51). Às 12h52, alguns caminhões (incluindo os de Bob) são carregados e descarregados, e as estruturas de dados são atualizadas, de modo que AllTrucks
agora haverá uma referência a uma estrutura de dados que indica que a carga está em todos os caminhões a partir das 12h52.
O que deveria acontecer BobsTruck
?
Se a propriedade 'carga' de cada objeto de caminhão for imutável, o objeto 24601 representará para sempre o estado que o caminhão de Bob tinha às 12h51. Se BobsTruck
tiver uma referência direta ao objeto # 24601, a menos que o código que atualiza AllTrucks
também seja atualizado BobsTruck
, ele deixará de representar o estado atual do caminhão de Bob. Observe ainda que, a menos que BobsTruck
seja armazenado em alguma forma de objeto mutável, a única maneira que o código que atualiza AllTrucks
poderia atualizá-lo seria se o código fosse explicitamente programado para isso.
Se alguém quiser usar BobsTruck
para observar o estado do caminhão de Bob e ainda manter todos os objetos imutáveis, poderia BobsTruck
ser uma função imutável que, dado o valor que AllTrucks
tem ou teve em um determinado momento, produzirá o estado do caminhão de Bob em esse tempo. Pode-se até ter um par de funções imutáveis - uma das quais seria como acima, e a outra aceitaria uma referência a um estado de frota e um novo estado de caminhão e retornaria uma referência a um novo estado de frota que combinava com o antigo, exceto que o caminhão de Bob teria o novo estado.
Infelizmente, ter que usar essa função sempre que alguém quiser acessar o estado do caminhão de Bob pode se tornar um pouco chato e complicado. Uma abordagem alternativa seria dizer que o objeto nº 24601 representará sempre e para sempre (desde que alguém tenha uma referência a ele) representará o estado atual do caminhão de Bob. O código que deseja acessar repetidamente o estado atual do caminhão de Bob não precisaria executar uma função demorada todas as vezes - ele poderia simplesmente fazer uma função de pesquisa uma vez para descobrir que o objeto # 24601 é o caminhão de Bob e, em seguida, simplesmente acesse esse objeto sempre que quiser ver o estado atual do caminhão de Bob.
Observe que a abordagem funcional não apresenta vantagens em um ambiente de thread único ou em um ambiente de múltiplos threads em que os threads geralmente apenas observam dados em vez de alterá-los. Qualquer encadeamento de observador que copia a referência de objeto contida emAllTrucks
e, em seguida, examina os estados de caminhão representados, assim, verá o estado de todos os caminhões no momento em que ele pegou a referência. Sempre que um encadeamento de observador quiser ver dados mais recentes, ele pode simplesmente pegar novamente a referência. Por outro lado, ter todo o estado da frota representado por um único objeto imutável impediria a possibilidade de dois encadeamentos atualizarem caminhões diferentes simultaneamente, uma vez que cada encadeamento, se deixado por conta própria, produziria um novo objeto de "estado da frota" que incluía o novo estado de seu caminhão e os antigos estados de todos os outros. A correção pode ser garantida se cada thread usar CompareExchange
para atualizar AllTrucks
apenas se não tiver sido alterado e responder a uma falhaCompareExchange
regenerando seu objeto de estado e tentando novamente a operação, mas se mais de um encadeamento tentar uma operação de gravação simultânea, o desempenho geralmente será pior do que se toda a gravação fosse feita em um único encadeamento; quanto mais threads tentarem operações simultâneas, pior será o desempenho.
Se objetos de caminhão individuais são mutáveis, mas têm identidades imutáveis , o cenário multithread torna-se mais limpo. Apenas um segmento pode operar de cada vez em um caminhão, mas os segmentos que operam em caminhões diferentes podem fazê-lo sem interferência. Embora existam maneiras de se imitar esse comportamento, mesmo ao usar objetos imutáveis (por exemplo, é possível definir os objetos "AllTrucks") para que a definição do estado do caminhão pertencente a XXX ao SSS exija simplesmente a geração de um objeto com o título "A partir de [Time], o estado do caminhão pertencente a [XXX] agora é [SSS]; o estado de todo o resto é [Valor antigo da AllTrucks] ". A geração de um objeto desse tipo seria rápida o suficiente para que, mesmo na presença de contenção, umCompareExchange
loop não demoraria muito. Por outro lado, o uso dessa estrutura de dados aumentaria substancialmente a quantidade de tempo necessária para encontrar o caminhão de uma determinada pessoa. O uso de objetos mutáveis com identidades imutáveis evita esse problema.