不透明类型解决的问题(上)
in 技术 with 0 comment

不透明类型解决的问题(上)

in 技术 with 0 comment

具有不透明返回类型的函数或方法隐藏其返回值的类型信息。不是提供具体类型作为函数的返回类型,而是根据它支持的协议来描述返回值。隐藏类型信息在模块和调用该模块的代码的连接处很有用,因为返回值的底层类型可以保持私有。与返回类型为协议类型的值不同,不透明类型保留类型标识——编译器可以访问类型信息,但调用该模块的代码却不能。

译者注解:这段话总结了不透明类型的作用。以及与协议类型的区别,协议类型会导致类型擦除,而不透明类型则不会。

返回不透明类型(中)

不透明类型解决的问题

例如,假设您正在编写一个绘制 ASCII 图形形状的模块。ASCII 图形形状的基本特征是一个 draw() 函数,它返回字符串以表示该形状,您可以将该函数定义为 Shape 协议的方法:

protocol Shape {
    func draw() -> String
}

struct Triangle: Shape {
    var size: Int
    func draw() -> String {
        var result: [String] = []
        for length in 1...size {
            result.append(String(repeating: "*", count: length))
        }
        return result.joined(separator: "\n")
    }
}
let smallTriangle = Triangle(size: 3)
print(smallTriangle.draw())
// *
// **
// ***

您可以使用泛型来实现像垂直翻转形状这样的操作,如下面的代码所示。然而,这种方法有一个重要的限制:公开了用于创建 FlippedShape 的确切泛型类型。

struct FlippedShape<T: Shape>: Shape {
    var shape: T
    func draw() -> String {
        let lines = shape.draw().split(separator: "\n")
        return lines.reversed().joined(separator: "\n")
    }
}
let flippedTriangle = FlippedShape(shape: smallTriangle)
print(flippedTriangle.draw())
// ***
// **
// *

如果要绘制一个梯形,则通过定义 JoinedShape<T: Shape, U: Shape> 这种结构体,将两个形状垂直连接在一起,如下面的代码所示,会产生 JoinedShape<Triangle, FlippedShape<Triangle>> 这样的类型,将翻转的三角形与另一个三角形连接起来。

struct JoinedShape<T: Shape, U: Shape>: Shape {
    var top: T
    var bottom: U
    func draw() -> String {
        return top.draw() + "\n" + bottom.draw()
    }
}
let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle)
print(joinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *

由于需要声明完整的返回类型,公开关于创建形状的详细信息,导致不属于 ASCII 图形模块的公共接口的类型泄漏。模块内部的代码可以以各种方式构建相同的形状,而调用该形状的模块外部的其它代码不必考虑有关形状构建的实现细节。像 JoinedShapeFlippedShape 这样的包装器类型对模块的使用者来说并不重要,而且它们不应该是可见的。该模块的公共接口由连接和翻转形状等操作组成,这些操作返回另一个 Shape 值。

参考:https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html

Responses