2014-12-27 4 views
1

Я работаю над приложением с Scala. Я пытаюсь реализовать обработку исключений в приложении. Я пытался использовать Either для обработки исключений. Для простых случаев, похоже, достаточно обработать исключения. Однако, когда мне нужно получать результаты из нескольких таблиц с помощью объединений, обработка исключений создает больше проблем, чем решение. Ниже описана моя структура приложения:Обработка исключений в scala

Использование шаблона репозитория для операций с базой данных, который определяет все операции взаимодействия с базой данных. Для, например, у меня есть центральный репозиторий, который определяет findAll, findById, insert, delete, update, exists и т.д. Мой findAll методы подпись была изменена на Either[CustomException, List[TEntity]], аналогично findById типа методов является Either[CustomException, Option[TEntity]].

Теперь предположит, что, если мне нужно забрать сотрудник из базы данных в моем методе, я буду делать что-то, как показано ниже:

def getVehicleById(id:Long) = { 

val vehicle = repository.findById(id) 
//i have one-to-one mapping with Employee table for the driver of the vehicle 
val driver = empRepository.findById(vehicle.driverId) 

} 

Теперь типа транспортного средства будет Either[CustomException, Option[Vehicle]] и тип водителя Either[CustomException, Employee]

Если мне нужно сделать больше операций после получения результатов, я должен использовать случай Right/Left, а затем сделать это. Проблема в том, что может быть внутри правильного случая, мне может потребоваться присоединиться к другой таблице, что приведет к ее результату снова Either[CustomException, Entity]. если в этой операции возникла какая-либо ошибка, мне нужно снова использовать блок catch try. Я чувствую, что этот вид hanling становится очень трудным для управления, когда сложность кода возрастает, а также у меня будет много кодовых табличек кодов для обработки этих ситуаций, что противоречит самому принципу Scala.

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

Примечание: Я исхожу из фона Java, работаю на Scala только пару месяцев.

EDIT

Добавление образца кода с Try: Этот пример очень близок к требованию у меня есть.

import scala.util._ 
def checkTry:Try[List[Int]] = Success(List(2)) 
def checkTryStr:Try[String] = Success("Asd") 

def getVehicleWithDriver = for { 
    a <- checkTry 
    c <- a 
    b <- checkTryStr 
}yield { 
    (c,b) 
} 
getVehicleWithDriver 

Но с кодом bove, я получаю ошибки компиляции.

Error:(9, 6) type mismatch; 
found : scala.util.Try[(Int, String)] 
required: scala.collection.GenTraversableOnce[?] 
    b <- checkTryStr 
    ^
Error:(9, 6) type mismatch; 
found : scala.util.Try[(Int, String)] 
required: scala.collection.GenTraversableOnce[?] 
    b <- checkTryStr 
    ^
Error:(8, 6) type mismatch; 
found : List[Nothing] 
required: scala.util.Try[?] 
    c <- a 
    ^
+0

Я не вижу имя 'Try' в вашем вопросе, так что вы можете знать об этом: http://www.scala-lang.org/files/archive/nightly/docs/ library/index.html # scala.util.Try –

+0

Извините, я забыл упомянуть об этом. Но не то же самое с попыткой. Вместо сопоставления с правым/левым, мне нужно сопоставить с Try и wouldnt, которые также создают очень большие вложенные заявления try? –

+0

Как насчет альтернативных методов сканирования? http://typelevel.org/blog/2014/02/21/error-handling.html –

ответ

2

Вы хотите использовать сахар for/yield. Вы можете либо привыкнуть поставить .right на все ваши Either s, или, как предлагает @Gangstead, используйте \/ от Scalaz.(Есть, конечно, страшные части Scalaz - я использую его в течение 4 лет и до сих пор запуганы, например, Strength - но вполне можно начать с простых деталей и проделать свой путь).

for { 
    vehicle <- repository.findById(id).right 
    driver <- empRepository.findById(vehicle.driverId).right 
    somethingElse <- somePossiblyFailingComputation.right 
} yield somethingElse 
//Don't need the .right if you're using Scalaz \/ 

Другие важные подсказки: когда вы хотите применить effectful функцию к списку, вы хотите использовать Scalaz traverse:

vehicleList <- vehicleIdList.traverse(repository.findById) //assuming you're using \/ 
//might need a few .right if you're using Either 

Когда вы вызываете Java код, который может вызвать исключение, использовать scala.util.control.Exception «s catching:

val myEither = catching(classOf[SomeException]) either someJavaMethodThatThrows() 
// (...).disjunction if you're using \/. 

Если вам нужно работать с другим видом„effectful контекста“в то же время, как работает с Either/\/, вам нужно будет начать смотреть на трансформаторы monad, но, возможно, было бы лучше, если вам всего лишь Either.

Надеюсь, что это поможет; удачи, надеюсь, вам понравится Scala.

+0

Спасибо за информацию. Но я беспокоюсь о том, что я должен всецело использовать везде, когда я использую Либо. И что, если это на самом деле Левое и неправильное, что есть в Либе. случай справа/слева везде. –

+1

Нет, '.right' будет корректно обрабатывать' Left' или 'Right', это просто синтаксис, чтобы корректно работать с картографией. Если вы не хотите, чтобы нужно было поставить'. (и я согласен с этим), используйте вместо этого '\ /' Scalaz, и вы можете использовать 'for' /' yield' без каких-либо '.right'. – lmm

1

Посмотрите на дизъюнкции Scalaz в \/.

Предупреждение: Скалас - это хардкорная скала, написанная хардкорными парнями.

Взгляните на презентацию Brendon McAdams '(@Rit) около "Scalaz' Gateway Drugs". Он гораздо более подходит для программистов, пришедших с Java, и он держит его очень практично.

+0

Да. Я пробовал понимать Scalaz, но он шел намного выше моей головы .. :( –

+0

Я рассматривал это только после того, как увидел презентацию Брендона. У него есть образец кода, и он выглядит очень полезным. Его пример - ваш прецедент: замена Eithers на обработку db lookup failures проще. – Gangstead

0

Вы хотите использовать Try, а не Either. Это монада, поэтому вы можете использовать map или flatMap или использовать ее в понимании for. На самом деле это выглядит довольно элегантно.

def getVehicleWithDriver(id: String): Try [(Vehicle, Driver)] = for { 
    vehicle <- repository. findById(id) 
    driver <- empRepository.findById(vehicle.driverId) 
} yield { 
    (vehicle, driver) 
} 
+1

Проблема с 'Try' заключается в том, что вы теряете тип безопасность - отказ может быть любым «Throwable». Лучше использовать scalaz '\ /'. – lmm

+0

Я не уверен, что я вижу ценность безопасности типов для сбоев. Вы можете использовать 'case e: SpecificException' при работе с определенными типами сбоев и позволить непревзойденным распространяться до вызывающего. Это стандартный способ обработки исключений. – Dima

+1

Значение безопасности типа для отказов совпадает с значением безопасности типа для любого другого значения. – lmm

Смежные вопросы