Соответствующий протокол в Swift - PullRequest
0 голосов
/ 09 июня 2018

Я пытаюсь сделать простую реализацию игры.Таким образом, каждая игра имеет правильный ответ.Ответ может быть Int или String.Итак, что у меня есть в коде:

protocol Answer {}
extension Int: Answer {}
extension String: Answer {}

protocol CorrectAnswer {
    var correctAnswer: Answer { get }
}

У меня есть протокол для того, что нужно игре:

protocol GameDescriber {
    var name: String { get }
    var description: String { get }
    var points: Int { get }
}

И реализация структуры Game:

struct Game: GameDescriber, Equatable, CorrectAnswer {
    var correctAnswer: Answer
    var name: String
    var description: String
    var points: Int

    static func ==(_ lhs: Game, _ rhs:Game) -> Bool {
        if let _ = lhs.correctAnswer as? String, let _ = rhs.correctAnswer as? Int {
            return false
        }

        if let _ = lhs.correctAnswer as? Int, let _ = rhs.correctAnswer as? String {
            return false
        }

        if let lhsInt = lhs.correctAnswer as? Int, let rhsInt = rhs.correctAnswer as? Int {
            if lhsInt != rhsInt {
                return false
            }
        }

        if let lhsString = lhs.correctAnswer as? String, let rhsString = rhs.correctAnswer as? String {
            if lhsString != rhsString {
                return false
            }
        }

        return lhs.description == rhs.description &&
            lhs.name == rhs.name &&
            lhs.points == rhs.points
    }
}

Если я хочу добавить другой тип ответа (скажем, массив Ints), я должен сделать это:

extension Array: Answer where Element == Int {}

Но что меня беспокоит, так это реализация Equatable func == У меня естьчтобы охватить этот и, возможно, другие случаи, а также.Что может сделать меня драматичным:)

Есть ли решение для этого и можно ли сделать это более изящным и общим способом?

1 Ответ

0 голосов
/ 09 июня 2018

Сначала обратите внимание, что ваша реализация == может быть упрощена до

static func ==(_ lhs: Game, _ rhs:Game) -> Bool {
    switch (lhs.correctAnswer, rhs.correctAnswer) {
    case (let lhsInt as Int, let rhsInt as Int):
        if lhsInt != rhsInt {
            return false
        }
    case (let lhsString as String, let rhsString as String):
        if lhsString != rhsString {
            return false
        }
    default:
        return false
    }
    return lhs.description == rhs.description &&
        lhs.name == rhs.name &&
        lhs.points == rhs.points
}

, так что добавление другого типа ответа просто означает добавление one дополнительного регистра.

Проблема в том, что компилятор не может проверить, что все возможные типы ответов обрабатываются в вашей функции ==, поэтому этот подход подвержен ошибкам.

Что я на самом деле сделал бы, так это использовалenum Answer вместо протокола и сделать , что Equatable:

enum Answer: Equatable {
    case int(Int)
    case string(String)
}

Обратите внимание, что вам не нужно реализовывать ==.Начиная с Swift 4.1, компилятор автоматически его синтезирует, см. SE-0185 Синтезирование Equatable и Hashable соответствия .

А теперь Game упрощается до

struct Game: GameDescriber, Equatable, CorrectAnswer {
    var correctAnswer: Answer
    var name: String
    var description: String
    var points: Int
}

, гдекомпилятор также синтезирует == с реализацией по умолчанию, которая сравнивает все сохраненные свойства на равенство.

Добавление другого типа ответа просто выполняется путем добавления другого регистра к перечислению:

enum Answer: Equatable {
    case int(Int)
    case string(String)
    case intArray([Int])
}

без дополнительного кода.

...