Какие проблемы возникают, если объект хранит свой ключ из внешней коллекции?

«Какие проблемы возникают, если объект хранит свой ключ из внешней коллекции?» — вопрос из категории Алгоритмы и структуры данных, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Хранение объектом ключа, который также используется для его идентификации во внешней структуре данных (например, ключ в словаре [Key: Value]), приводит к нескольким серьезным проблемам:

  1. Нарушение единственного источника истины (Single Source of Truth) Ключ становится дублированным данными. Если ключ в коллекции изменится, объект будет хранить устаревшее значение, что приведет к несогласованности состояния системы.

    struct Product {
        let sku: String // Дубликат ключа из словаря
        var name: String
    }
    var inventory: [String: Product] = ["A123": Product(sku: "A123", name: "Apple")]
    
    // Изменяем ключ в словаре
    let product = inventory.removeValue(forKey: "A123")
    inventory["B456"] = product // Ключ в словаре теперь "B456"
    // Но product.sku по-прежнему равен "A123" — несоответствие!
  2. Нарушение инвариантов коллекции Коллекции, основанные на хешах (как Dictionary или Set), полагаются на неизменность hashValue и Equatable для ключа/элемента. Если ключ является частью объекта и используется в hash(into:) и ==, то изменение этого ключа после помещения объекта в коллекцию сломает её внутреннюю структуру.

    var userSet = Set<User>()
    var user = User(id: 1, name: "Alice")
    userSet.insert(user) // Хэш вычислен от id=1
    user.id = 2 // Меняем ключевое свойство!
    // Теперь поиск `userSet.contains(user)` может дать неверный результат или сбой.
  3. Нарушение принципа единственной ответственности (SRP) Объект начинает знать о способе своего хранения в конкретной коллекции. Это связывает его логику с деталями реализации внешней структуры, снижая переиспользуемость и усложняя тестирование.

  4. Избыточность и увеличение расхода памяти Хранение лишнего поля увеличивает размер каждого объекта в памяти без реальной необходимости.

Решение: Ключ должен быть внешним идентификатором, управляемым коллекцией. Объект может предоставлять свойство для вычисления ключа (например, id), но не должен дублировать ключ коллекции. Для связи используйте значения, а не ключи (например, object.id как ключ в словаре [Int: Object]).