Ошибки - это просто значения. Вы можете манипулировать ими, не бросая их. throws
это просто модный вид return
. Он может работать с ошибками, сгенерированными так, как вам нравится.
Несмотря на то, что Sweeper делает хорошие замечания, и для приложения с командной строкой такой подход может быть лучшим, я часто считаю, что лучше регистрироваться в точке генерации ошибок, чем ошибка потребления. Я считаю, что это делает журналы намного более полезными, делая намного более понятным, где произошла первоначальная ошибка (я обычно записываю информацию о файлах и строках). Конечно, не все виды ошибок должны регистрироваться. Но по моему опыту генератор часто знает лучше, чем вызывающий.
Итак, вход в свою собственную функцию, которая принимает ошибку и возвращает ошибку.
func logging(_ error: LocalizedError) -> LocalizedError {
fputs("\u{001B}[0;31m\(error.errorDescription ?? "")\n", stderr)
return error
}
И теперь все ясно когда вы регистрируетесь, и вам не нужно дублировать код регистрации повсеместно.
func validate() throws {
guard ... else {
throw logging(ErrorList.alreadyInList)
}
}
Но что, если вы хотите изменить способ ведения журнала? А как насчет юнит-тестирования и все такое? Не проблема. В Swift функции первого класса, так что вы можете передавать их, и они могут даже иметь значения по умолчанию.
typealias Logger = (LocalizedError) -> LocalizedError
struct Operation {
let logging: Logger
init(logging: @escaping Logger = standardLogging) {
self.logging = logging
}
func validate() throws {
guard false else {
throw logging(ErrorList.alreadyInList)
}
}
// ...
}
И теперь вы можете создать Operation
с помощью стандартного регистратора тривиально, но для модульного тестирования Вы можете сделать это:
class LogAccumulator {
var logs: [Error] = []
func logging(_ error: LocalizedError) -> LocalizedError {
logs.append(error)
return error
}
}
let logs = LogAccumulator()
try? Operation(logging: logs.logging).validate()
print(logs.logs)
У меня был большой успех модульного тестирования путем проверки выходных данных журнала. (Я считаю, что вывод журнала является явной частью интерфейса, который должен быть протестирован вместе с другим поведением.) Он может позволить вам протестировать все виды вещей, которые сложно проверить, не публикуя личные данные c.