Как реализовать протокол Swift в структурах с конфликтующими именами свойств - PullRequest
3 голосов
/ 08 октября 2019

Я пытаюсь написать протокол, который совмещает две разные структуры, которые описывают одну и ту же концепцию, остановку какого-то рода. Оба имеют координаты Code, Description и Latitude & Longitude, но для одного типа Description может быть nil, а для другого типа координаты могут быть nil.

Как я могу написать один протокол, который согласовывает эти две структуры?

Вот мой протокол:

protocol Stop {
    var Code : String { get }
    var Description : String { get }
    var Latitude : Double { get }
    var Longitude : Double { get }
}

И два типа остановок:

struct BusStop : Stop {  // Compiler error: doesn't implement Description
    var Code : String
    var Description : String?
    var Latitude : Double
    var Longitude : Double
    // Various other properties
}

struct TrainStop : Stop {  // Compiler error: doesn't implement Latitude or Longitude
    var Code : String
    var Description : String
    var Latitude : Double?
    var Longitude : Double?
    // Various other properties
}

В C # (мой родной язык) я бы написал явную реализацию интерфейса следующим образом (псевдокод):

// At the end of the BusStop struct
var Stop.Description : String { return Description ?? string.Empty }

// At the end of the TrainStop struct
var Stop.Latitude : Double { return Latitude ?? 0 }
var Stop.Longitude : Double { return Longitude ?? 0 }

Однако я не знаю о каких-либо аналогичных функциональных возможностях в Swift. Учитывая, что я не могу изменить существующие определения свойств BusStop и TrainStop, как я могу написать протокол Stop так, чтобы он обернул обе структуры и возвращал свойства при их наличии?

Ответы [ 2 ]

2 голосов
/ 08 октября 2019

Желательная особенность явных реализаций интерфейса заключается в том, что они отправляются статически, верно? Если вы используете Description для BusStop, это будет необязательная строка, но если вы используете Description для Stop, это будет необязательная строка.

В Swift,члены расширения статически отправляются, поэтому вы можете использовать это для достижения чего-то похожего:

extension Stop where Self == BusStop {
    // Since the type of "self" here is BusStop, "Description" refers to the one declared in BusStop
    // not this one here, so this won't cause infinite recursion
    var Description : String { return self.Description ?? "" }
}

extension Stop where Self == TrainStop {
    var Latitude: Double { return self.Latitude ?? 0 }
    var Longitude: Double { return self.Longitude ?? 0 }
}

Этот код показывает, что это работает:

let busStop = BusStop(Code: "ABC", Description: "My Bus Stop", Latitude: 0, Longitude: 0)
print(type(of: busStop.Description)) // Optional<String>
let stop: Stop = busStop
print(type(of: stop.Description)) // String

Однако я до сих пор недумаю, что это хороший код Swift. Часто плохо просто переводить API с одного языка на другой. Если бы я был тобой, я бы сделал Longitude, Latitude и Description в Stop всеми опциональными.

0 голосов
/ 08 октября 2019

Я согласен с @Sweeper, возможно, ваш дизайн не самый лучший, если вы обнаружили необходимость объединить разные макеты данных под одним и тем же зонтиком. Тем не менее, я хочу поделиться другим возможным подходом к вашей проблеме.

Вместо того, чтобы иметь все свойства Stop в одном протоколе, вы можете поместить их в другую структуру и получить протокол Stop, возвращающий эту структуру:

protocol Stop {
    var stopData: StopData { get }
}

struct StopData {
    var code: String
    var stopDescription: String
    var latitude: Double
    var longitude: Double
}

Затем вы можете добавить следующие соответствия для ваших двух структур:

struct BusStop: Stop {
    var code: String
    var busStopDescription: String?
    var latitude: Double
    var longitude: Double

    var stopData: StopData {
        return StopData(code: code,
                        stopDescription: busStopDescription ?? "",
                        latitude: latitude,
                        longitude: longitude)
    }
}

struct TrainStop: Stop {
    var code: String
    var trainStopDescription: String
    var latitude: Double?
    var longitude: Double?

    var stopData: StopData {
        return StopData(code: code,
                        stopDescription: trainStopDescription,
                        latitude: latitude ?? 0,
                        longitude: longitude ?? 0)
    }
}

Это означает, что вы будете циркулировать StopData экземпляров в остальной части приложения вместопротокол Stop.

PS Я также изменил имена свойств, чтобы они больше соответствовали правилам именования Swift.

...