如何跨具有冲突属性名称的结构实现 Swift 协议



>我正在尝试编写一个协议来调和描述相同概念的两个不同结构,某种停止。两者都有CodeDescriptionLatitudeLongitude坐标,但对于一种类型,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 中有任何类似的功能。鉴于我无法更改BusStopTrainStop的现有属性定义,我如何编写Stop协议,使其环绕两个结构并在可用时返回属性?

显式接口实现所需的功能是它们是静态调度的,对吗?如果在BusStop上使用Description,它将是一个可选字符串,但如果在Stop上使用Description,它将是一个非可选字符串。

在 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 从一种语言翻译成另一种语言通常是不好的。如果我是你,我会LongitudeLatitudeDescriptionStop都是可选的。

我同意@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)
}
}

这意味着您将在应用程序的其余部分而不是Stop协议中传播StopData实例。

附言我还更改了属性名称,使其更符合 Swift 命名准则。

最新更新