1
resposta

Equatable não deveria ser preferencialmente estática?

Ao usar o gerador de stubs do XCode notei que ele inseriu o método == com um acessor estático, ao pesquisar na documentação da apple notei que se recomenda a mesma prática

To customize your type’s Equatable conformance, to adopt Equatable in a type that doesn’t meet the criteria listed above, or to extend an existing type to conform to Equatable, implement the equal-to operator (==) as a static method of your type.[...]

Colocar ele fora da classe neste caso não seria uma prática ruim?

Outro ponto citado pela documentação é que seria boa prática usar Equatable no sentido literal de substituição, onde um elemento que é igual ao outro poderia substituir ele em qualquer parte do código.

Equality implies substitutability—any two instances that compare equally can be used interchangeably in any code that depends on their values.[...]

e

[...]any of your custom types that conform to Equatable must satisfy three conditions, for any values a, b, and c:

  • a == a is always true (Reflexivity)
  • a == b implies b == a (Symmetry)
  • a == b and b == c implies a == c (Transitivity)[...]

Seria interessante reforçar que o principio da equidade não deveria ser usado para testar apenas um ou outro atributo, mas sim todos os atributos que não sejam diretamente ligados a identidade (no sentido de identidade do sistema (ex. ponteiro de referência))

1 resposta

"Colocar ele fora da classe neste caso não seria uma prática ruim?"

Sim. Quando criamos uma função fora de uma classe, dizemos que ela é global e pode ser modificada de qualquer ponto do código. E é aí que mora o perigo.

Usando o código como exemplo...

class Item : Equatable {

    let name: String
    let calories:Double

    init(name:String, calories:Double) {
        self.name = name
        self.calories = calories;
    }
}

func ==(first:Item, second:Item) -> Bool{
    return first.name == second.name && first.calories == second calories
}

... Para dois objetos do tipo Item, sempre que fizermos...

meuItem == outroItem

... será chamada a função == definida globalmente acima. Até aí, sem problemas. No entanto, em qualquer ponto do código é possível sobrescrever essa função, alterando sua regra de igualdade. Isso poderia causar problemas para quem já usava o ==.

Então, o melhor é proteger o == contra tentativas de sobrescrita. Para isso, podemos torná-la uma função estática dentro da classe Item:

class Item : Equatable {

    static func ==(first:Item, second:Item) -> Bool{
        return first.name == second.name && 
        first.calories == second calories
    }
}

Dessa forma, dizemos que ela não precisa de uma instância de Item para ser chamada (tal qual a função global) e não pode ser sobrescrita.

É por isso que a documentação sugere usar o == como uma função estática. Na realidade, qualquer função que não deve ser modificada deve indicar isso na sua assinatura.