Swift,结构。如何简化 struct multiple 属性 .dot 访问器?

Swift, struct. How to simplify struct multiple property .dot accessors?

我有一些表示地球上位置的结构,表示一个简单的 lat/lon 位置(Point2D), lat/lon/height location (Point3D), 以及机场等命名位置 姓名/lat/lon/身高位置(Airport).

struct Point2D {
  var lat: Double
  var lon: Double
}

struct Point3D {
  var point2D: Point2D
  var height_m: Double
}

struct Airport {
  var name: String
  var point3D: Point3D
}

这些结构按层次结构使用,效果很好,但在从机场实例中获取数据时确实会导致一些长的 .dot 访问。比如获取机场的纬度值是airport.point3D.point2D.lat,不太理想。

let airportLHR1 = Airport(name: "LHR", point3D: Point3D(point2D: Point2D(lat: 51.5, lon: -0.5), height_m: 25.0))
print("Airport: \(airportLHR1.name). Lat,Lon: \(airportLHR1.point3D.point2D.lat), \(airportLHR1.point3D.point2D.lon)")
// --> Airport: LHR. Lat,Lon: 51.5, -0.5

我已经将机场结构重构为 'flatten' 属性 .dot 访问,但我觉得可能有更好的方法来实现这一点。

struct Airport2 {
  var name: String
  private var _point3D: Point3D
  var lat:      Double  { get { _point3D.point2D.lat } set { _point3D.point2D.lat = newValue } }
  var lon:      Double  { get { _point3D.point2D.lon } set { _point3D.point2D.lon = newValue } }
  var height_m: Double  { get { _point3D.height_m }    set { _point3D.height_m = newValue } }

  init(name: String, lat: Double, lon: Double, height_m: Double) {
    self.name = name
    self._point3D = Point3D(point2D: Point2D(lat: lat, lon: lon), height_m: height_m)
  }
}

现在访问机场的数据要简单得多...

let airportLHR2 = Airport2(name: "LHR", lat: 51.5, lon: -0.5, height_m: 25.0)
print("Airport: \(airportLHR2.name). Lat,Lon: \(airportLHR2.lat), \(airportLHR2.lon)")
// --> Airport: LHR. Lat,Lon: 51.5, -0.5

...但是有更好的方法吗?

假设您用 height 表示高度,您可以简单地使用 CLLocation,并可选择使用便利初始化程序扩展它,让生活更轻松。

extension CLLocation {
    convenience init(lat: CLLocationDegrees, lon: CLLocationDegrees, alt: CLLocationDistance) {
        self.init(coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lon),
                  altitude: alt,
                  horizontalAccuracy: 0,
                  verticalAccuracy: 0,
                  timestamp: .now)
    }
}

struct Airport {
    let name: String
    let loc: CLLocation
}

let lax = Airport(name: "LAX", loc: CLLocation(lat: 33.9416, lon: -118.4085, alt: 39.0144))

print(lax.loc.coordinate.longitude) // -118.4085
print(lax.loc.altitude) // 39.0144

我建议:

struct Coordinate {
    let latitude: Double
    let longitude: Double
}

struct Location {
    let coordinate: Coordinate
    let altitude: Double
}

struct Airport {
    let name: String
    let location: Location
}

extension Airport {
    var latitude: Double { location.coordinate.latitude }
    var longitude: Double { location.coordinate.longitude }
}

以上,遵循最佳实践,我赞成不变性。但是如果你真的需要可变性:

struct Coordinate {
    var latitude: Double
    var longitude: Double
}

struct Location {
    var coordinate: Coordinate
    var altitude: Double
}

struct Airport {
    var name: String
    var location: Location
}

extension Airport {
    var latitude: Double {
        get { location.coordinate.latitude }
        set { location.coordinate.latitude = newValue }
    }

    var longitude: Double {
        get { location.coordinate.longitude }
        set { location.coordinate.longitude = newValue }
    }
}

无关,但我可能会将这些计算属性移动到 Place 协议中:

protocol Place {
    var name: String { get }
    var location: Location { get }
}

extension Place {
    var latitude: Double { location.coordinate.latitude }
    var longitude: Double { location.coordinate.longitude }
}

struct Airport: Place {
    let name: String
    let location: Location
}

这样您也可以定义机场以外的地方,而不必重复这些计算属性。