Swift: меньше кода с вложенными перечислениями, соответствующими равным протоколам - PullRequest
3 голосов
/ 25 апреля 2019

Я пытаюсь написать меньше кода в следующем сценарии:

У меня есть этот 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))
]

Теперь мне нужно получить элемент запроса url как в случае .pagination, так и .specific.

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 ]

2 голосов
/ 25 апреля 2019

Поскольку вы используете вложенные перечисления со связанными значениями, я действительно не вижу способа избежать использования этого дополнительного переключателя на верхнем уровне 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, где Element - это тип Queryable

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

// And now you can simply call
let queryItems = parameters.queryItems
1 голос
/ 25 апреля 2019

Без соответствия Parameters Queryable, вы можете просто ввести переменную в Parameters, чтобы получить queryItem, потому что оба случая принимают тип, который уже соответствует Queryable,

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

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