Раннее возвращение/золотой путь в Swift

Я привык писать код с ранним возвратом/золотым путем в Objective-C. Я попробовал этот подход в Swift и заметил, что ранний возврат происходит за счет использования оператора принудительной развертки (!), когда задействованы необязательные параметры.

Возьмем метод, который вычисляет размер каталога. Во-первых, версия золотого пути:

private func calculateSize_GoldenPath(directory:String) -> UInt64 {
    let fileManager = NSFileManager.defaultManager()
    var error : NSError?
    var contents = fileManager.contentsOfDirectoryAtPath(directory, error: &error) as [String]?
    if contents == nil {
        NSLog("Failed to list directory with error \(error)")
        return 0
    }
    var size : UInt64 = 0
    for pathComponent in contents! {
        let path = directory.stringByAppendingPathComponent(pathComponent)
        let attributes : NSDictionary? = fileManager.attributesOfItemAtPath(path, error: &error)
        if (attributes == nil) {
            NSLog("Failed to read file size of \(path) with error \(error)")
            continue
        }
        size += attributes!.fileSize()
    }
    return size;
}

Обратите внимание, как я использую оператор ! для переменных contents и attributes.

Я предполагаю, что чрезмерное использование оператора ! противоречит цели опционалов и безопасности типов, которые они обеспечивают. Вот как я считаю, что вышеуказанный метод должен быть закодирован в Swift, чтобы избежать принудительной развертки:

private func calculateSize_IfLet(directory:String) -> UInt64 {
    let fileManager = NSFileManager.defaultManager()
    var error : NSError?
    if let contents = fileManager.contentsOfDirectoryAtPath(directory, error: &error) as? [String] {
        var size : UInt64 = 0
        for pathComponent in contents {
            let path = directory.stringByAppendingPathComponent(pathComponent)
            if let attributes : NSDictionary = fileManager.attributesOfItemAtPath(path, error: &error) {
                size += attributes.fileSize()
            } else {
                NSLog("Failed to read file size of \(path) with error \(error)")
            }
        }
        return size
    } else {
        NSLog("Failed to list directory with error \(error)")
        return 0
    }
}

Однако, используя if let, я больше не могу досрочно возвращаться. Если некоторые методы не используют ранний возврат, а некоторые используют, то я получаю проект со смешанным стилем кодирования.

Мой вопрос: есть ли способ кодировать в стиле золотого пути, не прибегая к принудительной распаковке, когда задействованы необязательные параметры?


person hpique    schedule 25.08.2014    source источник
comment
Да, ! оператор принудительной развертки закончится и будет ненадлежащим образом использоваться в коде Swift. Хотя мне нравится золотой путь в сочетании с дополнительными опциями и принудительной распаковкой, код будет подвержен ошибкам распаковки при более поздних обновлениях, возможных другими. Лично я бы использовал извлечение методов. P.S. +1 за использование private!   -  person zaph    schedule 25.08.2014
comment
Я ненавижу ссылки, а не прямой ответ, но ответ оказывается длинным, потому что это целый способ подхода к проблеме, а не быстрый фрагмент кода. Но если вы найдете это полезным: robnapier.net/functional-wish-fulfillment. См. также nomothetis.svbtle.com/error-handling-in-swift, который использует очень похожий подход.   -  person Rob Napier    schedule 25.08.2014
comment
Хорошая статья @RobNapier. Из этого я делаю вывод, что золотой путь — это императивная концепция по своей сути, и, полностью охватывая функциональный аспект Swift, мы можем найти более подходящие решения. Часть меня хотела бы, чтобы Swift был выпущен с собственными системными фреймворками, как Microsoft сделала с .net, когда они выпустили C#.   -  person hpique    schedule 25.08.2014
comment
Забавно то, что то, на что я намекаю с continueWith (и объясню подробнее позже), на самом деле не полностью охватывает функциональность. Он использует метод, который переносит методы императивного программирования в функциональные языки. Причудливо отсталый, я знаю, но довольно мощный.   -  person Rob Napier    schedule 25.08.2014
comment
Но да, ваша основная мысль верна. Использование функций Swift, таких как перечисления со связанными данными и обобщениями, позволяет нам переосмыслить то, как мы справляемся с ошибками. Вместо того, чтобы относиться к ошибкам как к особому состоянию, от которого можно избавиться, мы относимся к ним просто как к результату, с которым нужно иметь дело.   -  person Rob Napier    schedule 25.08.2014


Ответы (1)


Лично я бы использовал извлечение методов, в этом случае извлеките раздел pathComponent в отдельный метод, избегая тем самым множественных отступов и неудобного кода, который смешивает концептуально отдельный код вместе.

private func calculateSize_IfLet(directoryPath:String) -> UInt64 {
    var size : UInt64 = 0
    let fileManager = NSFileManager.defaultManager()
    var error : NSError?
    if let contents = fileManager.contentsOfDirectoryAtPath(directoryPath, error: &error) as? [String] {
        size = self.calculateSizeOfDirectory(directoryPath, contents:contents)
    } else {
        NSLog("Failed to list directory with error \(error)")
    }
    return size
}

private func calculateSizeOfDirectory(directoryPath:String, contents:[String]) -> UInt64 {
    var size : UInt64 = 0
    for pathComponent in contents {
        var error : NSError?
        let fileManager = NSFileManager.defaultManager()
        let path = directoryPath.stringByAppendingPathComponent(pathComponent)
        if let attributes : NSDictionary = fileManager.attributesOfItemAtPath(path, error: &error) {
            size += attributes.fileSize()
        } else {
            NSLog("Failed to read file size of \(path) with error \(error)")
        }
    }
    return size
}
person zaph    schedule 25.08.2014