Исключение Scala как шаблон проектирования аргумента функции - PullRequest
1 голос
/ 22 декабря 2011

Я пишу веб-приложение, в котором исключения используются для обработки ошибок. Часто я пишу таких помощников:

def someHelper(...) : Boolean {...}

, а затем использовать его так:

if (!someHelper(...)){
    throw new SomeException()
}

Эти исключения представляют такие вещи, как недопустимые параметры, и при обработке они отправляют пользователю полезное сообщение об ошибке, например,

try {
     ...
} catch {
    case e: SomeException => "Bad user!"
}

Это разумный подход? И как я могу передать исключение в вспомогательную функцию и выбросить его туда? У меня были проблемы с созданием типа для такой функции.

Ответы [ 4 ]

6 голосов
/ 22 декабря 2011

Я использую Either большую часть времени, а не исключения.Я обычно использую исключения, как вы делали, или каким-то подобным образом, когда поток управления должен идти в обратном направлении, в какую-то далекую точку, а в остальном ничего толкового делать не приходится.Однако, когда исключения могут обрабатываться довольно локально, я вместо этого

def myMethod(...): Either[String,ValidatedInputForm] = {
  ...
  if (!someHelper(...)) Left("Agree button not checked")
  else Right(whateverForm)
}

, а затем, когда я вызываю этот метод, я могу

myMethod(blah).fold({ err =>
  doSomething(err)
  saneReturnValue
}, { form =>
  foo(form)
  form.usefulField
})

или сопоставлять на Left(err) против Right(form), или другие разные вещи.

Если я не хочу прямо сейчас обрабатывать ошибку, а вместо этого хочу обработать возвращаемое значение, я

myMethod(blah).right.map{ form =>
  foo(form)
  bar(form)
}

и яполучите Either с сообщением об ошибке, неизменным как Left, если оно было сообщением об ошибке, или с результатом { foo(form); bar(form) } как Right, если все в порядке.Вы также можете связать обработку ошибок с помощью flatMap, например, если вы хотите выполнить дополнительную проверку до сих пор правильных значений и отклонить некоторые из них, вы можете

myMethod(blah).right.flatMap{ form =>
  if (!checkSomething(form)) Left("Something didn't check out.")
  else Right(form)
}

Это такая обработкаэто делает использование Either более удобным (и обычно более эффективным, если исключения являются общими), чем исключения, поэтому я использую их.

(На самом деле, во многих случаях мне все равно почему что-то пошло не так, только , что пошло не так, и в этом случае я просто использую Option.)

1 голос
/ 22 декабря 2011

Два наиболее распространенных способа приблизиться к тому, что вы пытаетесь сделать, - это либо просто заставить помощника создать и выдать само исключение, либо именно то, что вы делаете: чтобы вызывающий код проверил результаты и бросил значимое исключение, если необходимо.

Я никогда не видел библиотеку, в которой вы передаете исключение, которое, как вы ожидаете, выдает помощник. Как я уже сказал в другом ответе , просто создание экземпляра исключения вызывает удивительно существенные затраты, и если вы будете следовать этому шаблону по всему коду, вы можете столкнуться с общей проблемой производительности. Этого можно избежать, используя параметры по имени , но если вы просто забудете добавить => в несколько ключевых функций, у вас возникнет проблема с производительностью, которую трудно отследить.

В конце дня, если вы хотите, чтобы помощник сгенерировал исключение, имеет смысл, что сам помощник уже знает, какое исключение он хочет сгенерировать. Если бы мне пришлось выбирать между А и В:

def helperA(...) { if (stuff) throw new InvalidStuff() }

def helperB(..., onError: => Exception) { if (stuff) throw onError }

Я бы выбрал A каждый раз.

Теперь, если бы мне пришлось выбирать между А и тем, что у вас есть сейчас, это бросок. Это действительно зависит от контекста, того, что вы пытаетесь достичь с помощью помощников, как еще их можно использовать и т. Д.

В заключение отметим, что наименование очень важно в подобных ситуациях. Если вы идете по пути код-помощника возврата, у ваших помощников должны быть имена вопросов, такие как isValid. Если у вас есть помощники, генерирующие исключения, они должны иметь имена действий, такие как validate. Может быть, даже придать ему акцент, как validate_!.

1 голос
/ 22 декабря 2011

Нет ничего особенного в передаче экземпляра исключения какому-либо методу:

def someMethod(e: SomeException) {
  throw e
}
someMethod(new SomeException)

Но я должен сказать, что у меня очень отчетливое ощущение, что вся ваша идея просто пахнет.Если вы хотите проверить пользовательский ввод, просто напишите валидаторы, например, UserValidator, у которого будет какой-то метод, например isValid, для проверки ввода пользователя и возврата логического значения, вы также можете реализовать там некоторые сообщения.Исключения действительно предназначены для разных целей.

0 голосов
/ 22 декабря 2011

Для альтернативного подхода вы можете проверить scalaz Валидаторы, которые дают большую гибкость для такого рода случаев (например, если я произвожу ошибку при сбое, накапливаю ошибки и сообщаю в конце или игнорирую их) полностью?). A несколько примеров может помочь вам решить, подходит ли вам этот подход.

Если вам трудно найти путь в библиотеку, этот ответ дает несколько указателей на некоторый вводный материал; или проверить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...