Pergunta

Eu li um par de artigos sobre a imutabilidade, mas ainda não seguem o conceito muito bem.

Eu fiz uma discussão sobre aqui recentemente que mencionou imutabilidade, mas como este é um tema em si mesmo, estou fazendo um segmento dedicado agora.

mencionei no segmento passado que eu pensei imutabilidade é o processo de fazer um objeto somente leitura e dando-lhe baixa visibilidade. Outro membro disse que não tinha nada a ver com isso. Este página (parte de uma série ) usa um exemplo de uma classe / struct imutável e ele usa somente leitura e outros conceitos para encerrar o assunto .

O que é exatamente a definição de Estado no caso deste exemplo? Estado é um conceito que eu não tenha realmente compreendido.

A partir de uma perspectiva de design diretriz, uma classe imutável deve ser um que não aceita a entrada do usuário e realmente só iria retornar valores?

O meu entendimento é que qualquer objeto que retorna apenas informações deve ser imutável e "bloqueado", certo? Então, se eu quiser retornar a hora atual em uma classe dedicada com que um método, que eu deveria usar um tipo de referência que irá funcionar uma referência do tipo e beneficiar, assim, I da imutabilidade.

Foi útil?

Solução

O que é imutabilidade?

  • A imutabilidade é aplicado principalmente para objectos (cordas, matrizes, uma classe animal personalizado)
  • Normalmente, se houver uma versão imutável de uma classe, uma versão mutável também está disponível. Por exemplo, Objective-C e cacau definir tanto uma classe NSString (imutáveis) e uma classe NSMutableString.
  • Se um objeto é imutável, ele não pode ser alterado depois de ser criado (basicamente somente leitura). Você pode pensar nisso como "apenas o construtor pode alterar o objeto".

Isto não tem directamente a ver com a entrada do usuário; nem mesmo seu código pode alterar o valor de um objeto imutável. No entanto, você sempre pode criar um novo objeto imutável para substituí-lo. Aqui está um exemplo pseudocódigo; nota que, em muitos idiomas você pode simplesmente fazer myString = "hello"; em vez de usar um construtor como eu fiz abaixo, mas eu incluí-lo para maior clareza:

String myString = new ImmutableString("hello");
myString.appendString(" world"); // Can't do this
myString.setValue("hello world"); // Can't do this
myString = new ImmutableString("hello world"); // OK

Você menciona "um objeto que apenas retorna informações"; isso não significa automaticamente torná-lo um bom candidato para a imutabilidade. objetos imutáveis ??tendem a retornar sempre o mesmo valor que eles foram construídos com, por isso estou inclinado a dizer a hora atual não seria ideal, uma vez que muda frequentemente. No entanto, você poderia ter uma classe MomentOfTime que é criado com um timestamp específico e sempre retorna que um timestamp no futuro.

Benefícios da Immutabilty

  • Se você passar um objeto para outra função / método, você não deve ter que se preocupar se esse objeto terá o mesmo valor após a função retorna. Por exemplo:

    String myString = "HeLLo WoRLd";
    String lowercasedString = lowercase(myString);
    print myString + " was converted to " + lowercasedString;
    

    E se a implementação de lowercase() mudou myString como ele estava criando uma versão em minúsculas? A terceira linha não iria lhe dar o resultado que queria. Claro, uma boa função lowercase() não faria isso, mas você está garantido este fato se myString é imutável. Como tal, objetos imutáveis ??pode ajudar a impor boas práticas de programação orientada a objetos.

  • É mais fácil fazer thread-safe um objeto imutável

  • É potencialmente simplifica a implementação da classe (bom se você é o único a escrever a classe)

Estado

Se você vier a tomar todas as variáveis ??de instância de um objeto e anote seus valores em papel, que seria o estado daquele objeto naquele determinado momento. O estado do programa é o estado de todos os seus objetos em um determinado momento. Estado muda rapidamente ao longo do tempo; um programa precisa de mudança de estado, a fim de continuar a execução.

Objetos imutáveis, no entanto, ter fixado estado ao longo do tempo. Uma vez criado, o estado de um objeto imutável não muda embora o estado do programa como um poder todo. Isto torna mais fácil de manter o controle do que está acontecendo (e veja outros benefícios acima).

Outras dicas

imutabilidade

Em termos simples, a memória é imutável quando não for modificado depois de ser inicializado.

Programas escritos em linguagens imperativas, como C, Java e C # pode manipular dados em memória à vontade. Uma área de memória física, uma vez posta de lado, pode ser modificado no todo ou em parte por um thread de execução, a qualquer momento durante a execução do programa. Na verdade, linguagens imperativas incentivar esta forma de programação.

programas de escrita desta forma tem sido incrivelmente bem sucedido para aplicações single-threaded. No entanto, como movimentos de desenvolvimento de aplicações modernas para com múltiplas threads simultâneos de operação dentro de um único processo, um mundo de problemas potenciais e complexidade é introduzida.

Quando há apenas um thread de execução, você pode imaginar que este único segmento 'possui' todos os dados na memória, e assim, portanto, pode manipulá-lo à vontade. No entanto, não existe o conceito implícito de propriedade quando vários threads em execução estão envolvidas.

Em vez disso, esse fardo recai sobre o programador que deve ir para um grande esforço para garantir que in-memory estruturas estão em um estado consistente para todos os leitores. construções de bloqueio deve ser utilizado em medida o cuidado de proibir um segmento de ver os dados enquanto ele está sendo atualizado por outro segmento. Sem esta coordenação, um fio inevitavelmente consumir dados que estavam apenas a meio sendo atualizado. O resultado de tal situação um é imprevisível e muitas vezes catastrófico. Além disso, tornar o trabalho de bloqueio corretamente no código é notoriamente difícil e quando mal feito pode prejudicar o desempenho ou, no pior dos casos, bloqueios de casos que a execução parada irremediavelmente.

Usando estruturas de dados imutáveis ??alivia a necessidade de introduzir bloqueio complexo em código. Quando uma seção de memória não é garantido para mudar durante o tempo de vida de um programa, em seguida, vários leitores podem acessar a memória simultaneamente. Não é possível para eles para observar que os dados particulares em um estado inconsistente.

Muitas linguagens de programação funcionais, tais como Lisp, Haskell, Erlang, F # e Clojure, incentivar as estruturas de dados imutáveis ??por sua própria natureza. É por esta razão que eles estão desfrutando de um ressurgimento do interesse enquanto nos movemos para desenvolvimento de aplicações multi-threaded e muitos por computador arquiteturas de computadores cada vez mais complexas.

Estado

O estado de um aplicativo pode simplesmente ser pensado como o conteúdo de todos os registros de memória e CPU em um determinado ponto no tempo.

Logicamente, o estado de um programa pode ser dividido em dois:

  1. O estado da pilha
  2. O estado da pilha de cada execução fio

Em ambientes gerenciados, como C # e Java, um segmento não pode acessar a memória de outro. Portanto, cada thread 'possui' o estado de sua pilha. A pilha pode ser pensado como segurando as variáveis ??locais e parâmetros de tipo de valor (struct), e as referências a objetos. Estes valores são isolados a partir de fios externos.

No entanto, os dados sobre a pilha é partilhável entre os tópicos, portanto, cuidados devem ser tomados para controlar o acesso simultâneo. Todas as ocorrências de objectos de referência de tipo (class) são armazenados na pilha.

Em OOP, o estado de uma instância de uma classe é determinada por seus campos. Estes campos são armazenados na pilha e por isso são acessíveis a partir de todos os tópicos. Se uma classe define métodos que permitem campos para ser modificado depois da conclusão do construtor, então a classe é mutável (não imutável). Se os campos não pode ser alterado de alguma forma, em seguida, o tipo é imutável. É importante notar que uma classe com todos / campos readonly Java C # final não é necessariamente imutável. Estas construções garantir a referência não pode mudar, mas não o objeto referenciado. Por exemplo, um campo pode ter uma referência imutável a uma lista de objetos, mas a co realntent da lista pode ser alterada a qualquer momento.

Ao definir um tipo como sendo verdadeiramente imutável, seu estado pode ser considerado congelado e, portanto, o tipo é seguro para o acesso de vários segmentos.

Na prática, pode ser inconveniente para definir todos os seus tipos como imutável. Para modificar o valor em um tipo imutável pode envolver um pouco de cópia de memória. Algumas línguas tornar esse processo mais fácil do que outros, mas de qualquer forma o CPU vai acabar fazendo algum trabalho extra. Muitos fatores contribuem para determinar se o tempo gasto copiando memória supera o impacto das contenções de bloqueio.

Um monte de investigação tem ido para o desenvolvimento de estruturas de dados imutáveis ??como listas e árvores. Ao usar tais estruturas, dizer uma lista, a operação 'Adicionar' irá retornar uma referência a uma nova lista com o novo item adicionado. As referências à lista anterior não vejo qualquer mudança e ainda ter uma visão consistente dos dados.

Para colocá-lo simples: Depois de criar um objeto imutável, não há nenhuma maneira de alterar o conteúdo desse objeto. Exemplos de objetos imutáveis ??.Net são de Cordas e Uri.

Quando você modificar uma string, você simplesmente obter uma nova string. A string original não vai mudar. Um Uri tem apenas propriedades somente leitura e há métodos disponíveis para alterar o conteúdo do Uri.

Os casos que objetos imutáveis ??são importantes são vários e na maioria dos casos tem a ver com segurança. O Uri é um bom exemplo aqui. (Por exemplo, você não quer um Uri que ser mudado por algum código não confiável.) Isso significa que você pode passar uma referência a um objeto imutável ao redor sem ter que se preocupar o conteúdo vai mudar.

Espero que isso ajude.

As coisas que são imutáveis ??nunca mudam. coisas mutáveis ??pode mudar. coisas mutáveis ??mutação. coisas imutáveis ??parecem mudar, mas realmente criar uma coisa nova mutável.

Por exemplo, aqui é um mapa em Clojure

(def imap {1 "1" 2 "2"})
(conj imap [3 "3"])
(println imap)

A primeira linha cria um novo mapa Clojure imutável. A segunda linha conjuga 3 e "3" para o mapa. Isto pode parecer como se está modificando o mapa antigo, mas na realidade ele está retornando um novo mapa com 3 "3", acrescentou. Este é um excelente exemplo de imutabilidade. Se isso tivesse sido um mapa mutável teria simplesmente adicionado 3 "3" diretamente para o mesmo mapa de idade. A terceira linha imprime o mapa

{3 "3", 1 "1", 2 "2"}

Imutabilidade ajuda a manter o código limpo e seguro. Esta e outras razões é porque linguagens de programação funcional tendem a inclinar-se para a imutabilidade e menos statefulness.

Boa pergunta.

Multi-threading. Se todos os tipos são não existem condições de corrida, em seguida, imutáveis ??e você está seguro para jogar o maior número de linhas no código como quiser.

Obviamente, você pode não conseguir isso muito sem mutabilidade salvar cálculos complexos para que você geralmente precisa de algum mutabilidade para criar software de negócios funcional. No entanto, vale a pena reconhecer onde imutabilidade deve ficar como nada transacional.

Olhe para cima de programação funcional e o conceito de pureza para obter mais informações sobre a filosofia. Quanto mais você armazenar na pilha de chamadas (os parâmetros que você está passando para métodos) em oposição a torná-los disponíveis através de referências como coleções ou estaticamente objetos disponíveis mais puras seu programa é e menos propensas a condições de corrida você será. Com mais multi-núcleos nos dias de hoje este tema é mais importante.

Além disso imutabilidade reduz a quantidade de possibilidades no programa que reduz o potencial complexidade e potencial para bugs.

Um objeto imutável é algo que você pode assumir seguramente não vai mudar; ele tem a propriedade importante que todos usá-lo pode assumir que eles estão vendo o mesmo valor.

Imutabilidade geralmente também significa que você pode pensar o objeto como sendo um "valor", e que não há nenhuma diferença efetiva entre cópias idênticas do objeto e do objeto em si.

Deixe-me acrescentar mais uma coisa. Além de tudo o que foi mencionado acima, você também quer imutabilidade para:

Fazendo coisas impede imutáveis ??um grande número de erros comuns.

Por exemplo, um estudante nunca deveria ter seu # mudança estudante sobre eles. Se você não fornecer uma maneira de definir a variável (e torná-lo const, ou final, ou qualquer que seja o seu suporte de idiomas), então você pode impor que em tempo de compilação.

Se as coisas são mutáveis ??e você não quer que eles mudam quando você passá-los em torno de que você precisa para fazer uma cópia defencive que você passa. Então, se o método / função que você chama muda a cópia do item original é intocável.

Fazendo coisas meio imutáveis ??você não tem que se lembrar (ou tomar o tempo / memória) para fazer cópias defencive.

Se você realmente trabalhar nisso, e pensar sobre cada variável que você faz, você vai achar que a grande maioria (I normalmente têm 90-95%) de suas variáveis ??nunca mudam, uma vez que é dado um valor. Fazendo isso torna os programas mais fáceis de seguir e reduz o número de erros.

Para responder à sua pergunta sobre estado, estado são os valores que as variáveis ??de um "objeto" (ser que uma classe ou um struct) ter. Se você tomou uma pessoa estado "objeto" seriam coisas como cor dos olhos, cor do cabelo, o comprimento do cabelo, etc ... alguns dos que (dizer a cor dos olhos) não mudam, enquanto outros, como o comprimento do cabelo mudam.

"... por que devo me preocupar com isso?"

Um exemplo prático é a concatenação repetitivo de cordas. Em .NET, por exemplo:

string SlowStringAppend(string [] files)
{
    // Declare an string
    string result="";

    for (int i=0;i<files.length;i++)
    {
        // result is a completely new string equal to itself plus the content of the new
        // file
        result = result + File.ReadAllText(files[i]);
    }

    return result;
}    

string EfficientStringAppend(string [] files)
{
    // Stringbuilder manages a internal data buffer that will only be expanded when absolutely necessary
    StringBuilder result=new SringBuilder();

    for (int i=0;i<files.length;i++)
    {
        // The pre-allocated buffer (result) is appended to with the new string 
        // and only expands when necessary.  It doubles in size each expansion
        // so need for allocations become less common as it grows in size. 
        result.Append(File.ReadAllText(files[i]));
    }

    return result.ToString();
}

Infelizmente usando a primeira abordagem da função (lento) ainda é comumente usado. Uma compreensão da imutabilidade deixa muito claro por que usar StringBuilder é tão importante.

Você não pode alterar um objeto imutável, portanto, você deve substituí-lo .... "para mudá-la". isto é, substituir, em seguida, descartar. "Substituir" neste meio dos sentidos mudando o ponteiro de um local de memória (do valor antigo) para outro (para o novo valor).

Note que ao fazê-lo agora usando memória adicional. Alguns para o valor antigo, alguns para o novo valor. Também nota, algumas pessoas ficam confusas porque eles olham para código, tais como:

string mystring = "inital value";
mystring = "new value";
System.Console.WriteLine(mystring); // Outputs "new value";

e pensam em si mesmos, "mas eu estou mudando isso, olhar ali mesmo, no 'novo valor' preto e branco! Saídas mystring ...... Eu pensei que você disse que eu não poderia mudar isso? !!"

Mas, na verdade sob o capô, o que está acontecendo é essa alocação de memória nova ou seja mystring agora está apontando para um endereço de memória diferente e espaço. "Imutável", neste sentido, não está se referindo ao valor de mystring mas sim a memória usada pela variável mystring para armazenar o seu valor.

Em certas línguas da memória que armazena o valor antigo devem ser limpos manualmente ou seja, o programador deve liberar explicitamente-lo ..... e lembre-se de fazê-lo. Em outras línguas este é um recurso automático da linguagem ou seja, a coleta de lixo em .Net.

Um dos lugares isso realmente sopra re: uso de memória é em loops altamente iterativos, especialmente com cordas como na pós ASHS'. Digamos que você estava construindo uma página HTML em um loop iterativo, onde você constantemente anexado o próximo bloco HTML para o último e, apenas por diversão, você estava fazendo isso em um servidor de alto volume. Essa alocação constante de "nova memória de valores" pode rapidamente sair caro e, finalmente, fatal, se a "memória valor antigo", não está devidamente limpo.

Outro problema é que algumas pessoas assumem coisas como coleta de lixo (GC) acontece imediatamente. Mas isso não acontece. Existem várias optimizações que ocorrem de modo a que a recolha de lixo está definido para ocorrer durante os períodos ociosos mais. Então, pode haver um atraso significativo entre o momento em que a memória é marcado como descartado e quando for realmente liberado pelo coletor de lixo .... para que você possa sofrer grandes picos de uso de memória se você simplesmente adiar o problema para o GC.

Se o GC não ter a chance de operar antes de executar fora de memória, então as coisas não ficará necessariamente cair como em outras línguas que não têm coleta de lixo automática. Ao invés, a GC vai chutar como o maior processo de prioridade para liberar a memória descartado, não importa o quão ruim o tempo, e tornar-se um processo de bloqueio, enquanto ele limpa as coisas. Obviamente, isso não é legal.

Então, basicamente, você precisa de código com estas coisas em mente e olhar para a documentação para os idiomas que você está usando para as melhores práticas / padrões que permitem que você para evitar / mitigar este risco.

Como em pós ASHS', em .Net e com cordas, a prática recomendada é usar a classe StringBuilder mutável, em vez das classes de cordas imutáveis ??quando se trata da necessidade de mudar constantemente um valor strings.

Outros idiomas / tipos será semelhante têm as suas próprias soluções.

Por imutabilidade?

  1. Eles são menos propensas a erros e são mais seguros.

  2. Classes imutáveis ??são mais fáceis de projetar, implementar e usar do que as classes mutáveis.

  3. Objetos imutáveis ??são thread-safe por isso não há problemas de sincronização.

  4. Objetos imutáveis ??são boas chaves Mapa e Ajuste elementos, uma vez que estes normalmente não mudam, uma vez criado.

  5. A imutabilidade torna mais fácil de escrever, uso e raciocinar sobre o código (classe invariante é estabelecida uma vez e, em seguida, inalterado).

  6. A imutabilidade torna mais fácil para paralelizar programa como não existem conflitos entre os objetos.

  7. O estado interno do programa será consistente, mesmo se você tem exceções.

  8. As referências a objetos imutáveis ??pode ser armazenado em cache como eles não vão mudar. (Isto é, em hash que fornecem operações rápidas).

Veja meu blog para uma resposta mais detalhada:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability. html

Olha, eu não li os links que você postou.

No entanto, aqui é o meu entendimento.
Cada programa possui algum conhecimento de TI de dados (Estado), que pode mudar tanto por user-input / mudanças externas etc.

Variáveis ??(valores que mudança) são mantidos para manter o estado. meios imutáveis ??alguns dados que não muda. Pode-se dizer, é mesmo como somente leitura ou constante de alguma forma (pode ser visto dessa forma).

AFAIK, programação funcional tem coisas imutáveis ??(ou seja, você não pode usar atribuição a uma variável mantendo o valor. O que você pode fazer é criar uma outra variável que pode conter o valor original + alterações).

.net tem classe string que é um exemplo.
ou seja, você não pode modificar seqüência em seu lugar

string s = "Olá"; Eu posso escrever s.Replace ( "el", "a"); Mas isso não vai modificar o conteúdo da variável s.

O que posso fazer é s = s.Replace ( "el", "a");
Isto irá criar uma nova variável e atribuir o seu valor para s (substituindo o conteúdo de s).

Os especialistas podem corrigir erros se eu tenho, no meu entendimento.

EDIT: (talvez) Imutável = unassignable uma vez que ele está segurando algum valor e não pode ser substituído no lugar

Um exemplo dos potenciais benefícios de desempenho oferecidos por objetos imutáveis ??está disponível na API WPF. A classe base comum de muitos tipos WPF é Freezable.

Vários exemplos WPF sugerem que o congelamento objetos (tornando-os imutáveis ??no tempo de execução) pode melhorar o desempenho do aplicativo significativamente como bloqueio e copiar não é necessária.

Pessoalmente eu desejo que o conceito de imutabilidade era mais fácil de expressar na linguagem que eu uso na maioria das vezes, C #. Há um modificador readonly disponível para campos. Eu gostaria de ver um modificador readonly em tipos bem que só seria permitida para os tipos que só têm campos somente leitura que são de tipos de somente leitura. Essencialmente, isso significa que todos os estados que necessitam de ser injectados no tempo de construção, e que e objecto gráfico inteiro seria congelado. Imagino que eram esses metadados intrínseca ao CLR, em seguida, que poderia ser facilmente usado para análise de otimizar lixo para GC.

Desculpe, por que imutabilidade evitar condições de corrida (neste exemplo, escrita após perigos de leitura)?

shared v = Integer(3)
v = Integer(v.value() + 1) # in parallel

A imutabilidade é sobre valores, e os valores são sobre fatos. Algo tem valor se é imutável, porque se algo pode ser mudado, então isso significa que nenhum valor específico pode ser conectado a ele. Objeto foi inicializado com o estado A e durante a execução do programa foi mutado para estado B e estado C. Isso significa que objeto não representa valor específico único, mas é apenas um recipiente, abstração sobre um lugar na memória, nada mais. Você não pode ter a confiança a tal recipiente, você não pode acreditar que este recipiente tem o valor que você suponha que deveria ter.

Deixe-nos ir ao exemplo - vamos imaginar essa instância no código é criado de classe Book.

Book bookPotter =  new Book();
bookPotter.setAuthor('J.K Rowling');
bookPotter.setTitle('Harry Potter');

Esta instância tem alguns campos definidos como autor e título. Tudo é ok, mas em alguma parte do código novamente setters são utilizados.

Book bookLor =  bookPotter; // only reference pass
bookLor.setAuthor('J.R.R Tolkien');
bookLor.setTitle('Lords of The Rings');

Do not ser enganado por diferentes nome de variável, na verdade é a mesma instância. O código está usando setters na mesma instância novamente. Isso significa que bookPotter nunca foi realmente o livro Harry Potter, bookPotter só é ponteiro para o local onde livro desconhecido está localizado. Dito isto, parece que ele é mais uma prateleira, em seguida, o livro. O que confiar em você pode ter a tal objeto? É Harry Potter livro ou livro LoR ou nenhum dos dois?

instância mutável de uma classe só é um ponteiro para um estado desconhecido com as características de classe.

Como, então, evitar a mutação? É bastante fácil nas regras:

  • objeto construção com o estado queria via construtor ou construtor
  • não criar setters para o estado encapsulado do objeto
  • não muda qualquer estado encapsulado do objeto em qualquer um dos seus métodos

Estas poucas regras permitirão ter objetos mais previsíveis e mais confiáveis. De volta ao nosso exemplo e livro seguinte acima regras:

Book bookPotter =  new Book('J.K Rowling', 'Harry Potter');
Book bookLor = new Book('J.R.R Tolkien', 'Lord of The Rings');

Tudo é definido durante a fase de construção, neste caso construtor, mas para estruturas maiores, pode ser um construtor. não existem setters em objetos, o livro não pode sofrer mutação para uma diferente. Nesse caso bookPotter representa o valor de Harry Potter livro e você pode ter certeza que isso é verdade imutável.

Se você está interessado em âmbito mais amplo de imutabilidade, neste artigo médio é mais sobre o assunto em relação ao JavaScript - https://medium.com/@macsikora/the-state-of-immutability-169d2cd11310 .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top