Padrões de codificação: cópia defensiva

5

Enquanto assistia a um vídeo do youtube sobre Tipos de valor no Swift , fiquei surpreso com um exemplo simples ( em torno da marca de 3 minutos) que foi dada para demonstrar as armadilhas dos tipos de referência.

Exemplo de código:

let home = House()
let temp = Temperature()
temp.fahrenheit = 75
home.thermostat.temperature = temp.copy()

temp.fahrenheit = 425
home.oven.temperature = temp.copy()
home.oven.bake()

No código acima, House e Temperature são classes e, portanto, têm semântica do tipo de referência. A justificativa para invocar o método copy () em vários lugares é assegurar que a mutação da variável temp para a configuração do forno não modifique a temperatura do termostato da casa (isto é, evite a mutação não intencional devido à semântica de referência).

Eu pessoalmente acho isso um estilo de codificação muito estranho, mas o apresentador continua por vários minutos sobre como essa assim chamada "cópia defensiva" é usada nas principais bibliotecas da Apple para evitar bugs, então estou pensando se já estive fazendo coisas erradas.

Pessoalmente, eu teria escrito isso nas seguintes linhas:

let home = House()
home.thermostat.temperature = Temperature()
home.thermostat.temperature.fahrenheit = 75   //actually I would personally have supplied this as a parameter to the initializer but I'm trying to remain as close to the original code as possible.

home.oven.temperature = Temperature()
home.oven.temperature.fahrenheit = 425
home.oven.bake()

Em outras palavras, eu criaria explicitamente novas instâncias da classe Temperature chamando seu inicializador (construtor) em vez de reutilizar uma variável local desse tipo e atribuindo cópias dela. Não apenas evita a última cópia, que é inútil, mas o código parece mais claro para mim.

Agora concedido, este é um exemplo trivial. No caso de uma classe complexa com muitos membros que compartilham os mesmos valores entre as instâncias, a recriação de um novo tipo a partir do zero e a atribuição de cada valor repetidamente é uma grande sobrecarga de programação. No entanto, na minha experiência, pelo menos, eu geralmente criei inicializadores ou métodos de fábrica que executam essas atribuições comuns, então eu só tenho que fazer algumas alterações quando necessário.

Nos casos raros em que o acima não se aplica, acho que chamar explicitamente um método clone () ou copy () é bom. Por fazer isso raramente, transmite para mim o fato de que algo especial está acontecendo (por exemplo, uma cópia em profundidade de uma árvore, por exemplo).

Portanto, minha pergunta é: o código acima da Apple é uma prática recomendada? E se não, em que circunstâncias (além de cópias profundas) a cópia manual como acima seria a maneira preferida de fazer as coisas?

    
por Dragonspell 30.03.2016 / 21:54
fonte

2 respostas

4

Concordo com Phil Lello que isso provavelmente foi simplificado demais, possivelmente ao ponto de ser confuso. Um exemplo muito mais claro da necessidade e indesejabilidade de (precisar fazer) cópia defensiva é o seguinte usando C # para concretude.

class House {
    private Thermostat thermostat;
    private ControlDisplay cc;
    // ...
    public void ShowDisplay() {
        // ...
        cd.SetTemperatureField(thermostat.Temperature.Clone());
        // ...
        cd.UpdateDisplay();
    }
}

Agora, se Temperature for mutável, toda vez que quisermos mostrar o display de controle, precisamos copiar a temperatura do termostato, mesmo que seja improvável que seja alterado pelo display. Mesmo se nós controlarmos o código para ControlDisplay e, portanto, pudermos verificar, olhando para o código-fonte que ele não sofreu mutação, o passado em Temperature , nós ainda copiamos porque isso pode ser alterado no futuro. Não havia como comunicar e impor que não deveria ser alterado.

Se Temperature fosse imutável, por outro lado, não haveria necessidade dessa cópia. O resultado seria um código mais limpo e eficiente, que simplesmente impossibilita certos erros e acoplamentos.

Eu imagino que a maioria das instâncias nas bibliotecas principais da Apple são mais parecidas com as anteriores do que com o exemplo fornecido no vídeo. Eu posso apenas imaginar alguns cenários, um tanto estranhos, onde código como o exemplo original poderia ser preferível para codificar mais como seu segundo exemplo (ou seu aludido para reescrever).

    
por 31.03.2016 / 00:33
fonte
4

Então eu assisti o vídeo. O exemplo inicialmente dado está além do ridículo. Se alguma coisa, o uso primário deve ser um exemplo de um bug no MyFirstProgram devido à reutilização de variáveis. Os exemplos posteriores (que foram encobertos) ilustram mais diretamente os usos para cópias defensivas.

No entanto, a cópia defensiva não era o objetivo do vídeo. Nem o exemplo (cópia original ou defensiva) codifica um modelo a ser seguido. E, de fato, o apresentador até diz que começa às 3:59 no vídeo. Ele está basicamente criando um problema (mal) para explicar como eles resolveram isso com o Swift. Se você olhar para as 19:45, ele mostra que o código de exemplo não seria compilado no Swift. E a partir das 20:03 ele mostra que o método de cópia defensiva não é necessário. (Embora seu exemplo pareça terrivelmente semelhante ao código original ... com um let alterado para var . O :) Ao longo do caminho ele também consegue superar Haskell com um algoritmo escolhido a dedo e uma aceitação singular critérios.

Em resumo, o take-away de segmentos do vídeo com esse código é:

  • Você deve não estar escrevendo código como no exemplo
  • Se você escrever um código como este, ele não será compilado no Swift
  • O código de cópia defensiva subseqüente "corrige o problema de referência", mas é muito trabalho (3:59)
  • Você não precisa escrever esse código copiado defensivo devido à maneira como o Swift implementa a semântica de valores
  • Os exemplos nesta apresentação não são muito bons.

Meu palpite sobre os pensamentos que levaram a esta apresentação.

  • "Você sabe, nós realmente deveríamos encorajar uma reutilização mais variável".
  • "Se mais uma pessoa perguntar por que Swift não tem o paradigma funcional, eu vou vasculhar completamente o FP na WWDC."
por 31.03.2016 / 00:50
fonte