2014-02-21 12 views
2

т.д .:Как безопасно передавать строковые значения?

def updateAsinRecords(asins:Seq[String], recordType:String) 

Выше метод принимает Seq из ASINs и тип записи. Оба имеют тип String. Существуют также другие значения, которые передаются с типом String в приложении. Излишне говорить, что это Scala, я бы хотел использовать систему типов в свою пользу. Как передавать строковые значения безопасным типом (например, ниже)?

def updateAsinRecords(asins:Seq[ASIN], recordType:RecordType) 
           ^    ^

Я могу себе представить, имея что-то вроде этого:

trait ASIN { val value:String } 

, но мне интересно, если есть лучший подход ...

ответ

3

Существует отличная бит новой функциональности Scala, которая известна как Value Classes and Universal Traits. Они не накладывают никаких накладных расходов во время выполнения, но вы можете использовать их, чтобы работать в безопасной манере типа:

class AnsiString(val inner: String) extends AnyVal 
class Record(val inner: String) extends AnyVal 

def updateAnsiRecords(ansi: Seq[AnsiString], record: Record) 

Они были созданы специально для этой цели.

+0

две ноты: 1) вы можете иметь их быть случай классов, 2) убедитесь, что вы падаете 'extends AnyVal', если вы добавите дополнительные поля. – dhg

+0

@dhg Нет, вам не разрешено иметь дополнительные поля в классе значений. Допускается только одно поле, период. – wheaties

+0

Справа. Это то, что я сказал. :-) – dhg

3

Вы можете добавить тонкие упаковщики с тематическими классами:

case class ASIN(asin: String) 
case class RecordType(recordType: String) 

def updateAsinRecords(asins: Seq[ASIN], recordType: RecordType) = ??? 

updateAsinRecords(Vector(ASIN("a"), ASIN("b")), RecordType("c")) 

Это не только сделает ваш код более безопасным, но и облегчит его чтение! Другим большим преимуществом такого подхода является то, что рефакторинг позже будет намного проще. Например, если позже вы решите, что ASIN должен иметь два поля вместо одного, то вы просто обновляете определение класса ASIN вместо каждого используемого им места. Аналогичным образом, вы можете делать такие вещи, как добавлять методы к этим типам, когда вы решите, что они вам нужны.

0

Как насчет типа псевдонима?

type ASIN = String 

обновление четкости (asins: Seq [ASIN])

+5

Это не более типичное. Вы все равно можете использовать любую 'String', где ожидается' ASIN'. –

3

В дополнение к предложениям о использовании Value Class/extends AnyVal, вероятно, вы должны контролировать строительство, чтобы только действительные случаи, так как предположительно не любой старая строка является действительной ASIN. (И ... это что-то вроде Amazon? Это как-то звонит колокол.)

Лучший способ сделать это - сделать частный конструктор и поместить валидационный заводский метод в объект-компаньон. Причина этого заключается в том, что бросание исключений в конструкторах (при попытке создать экземпляр с недопустимым аргументом) может привести к запутывающим режимам отказа (я часто вижу, что он проявляется в ошибке NoClassDefFoundError при попытке загрузить другой класс).

Таким образом, в дополнение к:

case class ASIN private (asin: String) extends AnyVal { /* other stuff */ } 

Вы должны включать в себя что-то вроде этого:

object A { 
    import scala.util.{Try, Success, Failure} 

    def fromString(str: String): Try[ASIN] = 
    if (validASIN(str)) 
     Success(new ASIN(str)) 
    else 
     Failure(new InvalidArgumentException(s"Invalid ASIN string: $str") 
} 
Смежные вопросы