Swift:更少的代码具有符合相等协议的嵌套枚举用例

时间:2019-04-25 12:32:02

标签: swift generics enums

在以下情况下,我尝试编写更少的代码:

我有这个Queryable协议和一个Parameter枚举:

protocol Queryable {
    var urlQuery: URLQueryItem { get }
}

enum PaginationParameter: Queryable {
    case page(Int)
    case pageSize(Int)

    var queryItem: URLQueryItem {
        switch self {
        case .page(let page):
            return URLQueryItem(name: "page", value: page.description)
        case .pageSize(let pageSize):
            return URLQueryItem(name: "page_size", value: pageSize.description)
        }
    }
}

还有一个枚举,它提供了一些默认情况和一些由通用类型定义的特定情况:

enum Parameter<P: Queryable> {
    case pagination(PaginationParameter)
    case specific(P)
}

用法示例

enum BookParameters: Queryable {
    case search(String)
    case id(Int)

    var urlQuery: URLQueryItem {
        switch self {
        case .search(let string):
            return URLQueryItem(name: "search", value: string)
        case .id(let id):
            return URLQueryItem(name: "id", value: id.description)
        }
    }
}

let parameters: [Parameter<BookParameters>] = [
    .pagination(.pageSize(10)),
    .specific(.id(123))
]

现在,我需要通过.pagination.specific两种情况来获取url查询项。

let queryItems = parameters.map({
    switch $0 {
    case .pagination(let param):
        return param.queryItem
    case .specific(let param):
        return param.queryItem
    }
})

最好有一种方法来处理组合在一起的嵌套案例,因为它们遵循相同的协议。这是行不通的,因为我必须遍历父案例的嵌套案例:

一个小改进是将switch语句埋入Parameters枚举的扩展中,并使它也符合Queryable协议:

extension Parameters: Queryable {

    let queryItem: URLQueryItem {
        switch self {
        case .pagination(let param):
            return param.queryItem
        case .specific(let param):
            return param.queryItem
        }
    }
}

这导致了一个班轮,但我只是将问题转移到了另一个地方。

let queryItems = parameters.map({ $0.queryItem })

2 个答案:

答案 0 :(得分:2)

由于您正在使用带有关联值的嵌套枚举,因此我真的没有办法避免在顶级Parameter枚举上进行此额外的切换。就我而言,Swift并没有为我们提供一种工具来处理案例,因为我们无法将具有“相同”关联值类型的所有案例转换为单个案例。您可以做的是重新考虑Parameter类型的存在,因为由于您仍然需要将其称为Parameter<BookParameters>Parameter<SomeOtherTypeThatConformsToQueryable>的事实,它似乎并没有真正的用处。

我个人会跳过顶级枚举,直接将parameters属性类型称为[Queryable]

var parameters: [Queryable] = [
    PaginationParameter.pageSize(10),
    BookParameters.id(123)
]

使事情变得更加简单和容易。现在,还有一种方法可以添加其他类型的其他案例,而您的初始解决方案是不可能的。

enum SomeOtherTypeThatConformsToQueryable: Queryable {
    case aVeryNiceCase(Int)
}

parameters.append(SomeOtherTypeThatConformsToQueryable.aVeryNiceCase(0))
// Appending this to array of type `[Parameter<BookParameters>]`, would not be
// possible without explicitly adding new case to the `Parameter` enumeration

如果您发现自己经常打电话给map { $0.queryItem },则可以提供Array的扩展名,其中ElementQueryable的类型

extension Array where Element==Queryable {
    var queryItems: [URLQueryItem] { return map { $0.queryItem } }
}

// And now you can simply call
let queryItems = parameters.queryItems

答案 1 :(得分:1)

如果不使Parameters符合Queryable,则只需在Parameters中引入变量来获取queryItem,因为两种情况都接受已经符合{{1 }},

Queryable