Идиоматический способ использования Options в Scala - PullRequest
5 голосов
/ 28 января 2011

Я конвертирую некоторый Java-код в Scala, пытаясь сделать код настолько идиоматичным, насколько это возможно.

Итак, теперь у меня есть некоторый код, использующий Options вместо значений Nullable, и мне интересно, являются ли вещи scala 'или я ошибаюсь.Итак, вы, ребята, не могли бы вы критиковать следующий фрагмент кода?

Области, в которых я специально ищу обратную связь:

  • Использование объекта-компаньона в качестве фабрики,давая 2 варианта в зависимости от того, хотим ли мы передать Options или Strings: хорошо ли конструктор String, или мы всегда должны раскрывать тот факт, что это Option?
  • Использование предварительных условий: есть ли лучшие способы утверждатьтот факт, что alpha3Code и name являются обязательными, и для alpha2Code должна быть передана ненулевая опция?(Я использую Guava для строковых утилит, поскольку я ничего не нашел в Scala API)
  • Реализация hashCode, equals и toString.equals и toString делегируют Guava снова, тогда как equals использует сопоставление с образцом.Есть ли более скалярный способ?
  • Я знаю, что мог бы использовать классы Case, которые бы создавали реализации по умолчанию, но меня больше всего интересует, как мне реализовать их для случаев, когда классы case не могут быть использованы.

Большое спасибо!

package com.sirika.openplacesearch.api.language

import com.google.common.base.Objects
import com.google.common.base.Strings

object Language {
    def apply(name : String, alpha3Code : String, alpha2Code : Option[String]) = new Language(name, alpha3Code, alpha2Code)
    def apply(name : String, alpha3Code : String, alpha2Code : String = null) = new Language(name, alpha3Code, Option(alpha2Code))
    def unapply(l : Language) = Some(l.name, l.alpha3Code, l.alpha2Code )
}


class Language(val name : String, val alpha3Code : String, val alpha2Code : Option[String]) {
    require(!Strings.isNullOrEmpty(alpha3Code))
    require(!Strings.isNullOrEmpty(name))
    require(alpha2Code != null)

    override def hashCode(): Int = Objects.hashCode(alpha3Code)

            override def equals(other: Any): Boolean = other match {
        case that: Language => this.alpha3Code == that.alpha3Code
        case _ => false
    }

    override def toString() : String = Objects.toStringHelper(this)
        .add("name", name)    
        .add("alpha3", alpha3Code)
        .add("alpha2", alpha2Code)
        .toString()
}

Ответы [ 3 ]

4 голосов
/ 28 января 2011

Вам следует избегать null, если нет веских причин не делать этого. Как бы то ни было, вы могли бы просто написать это:

def apply(name : String, alpha3Code : String, alpha2Code : String) = new Language(name, alpha3Code, Option(alpha2Code))
def apply(name : String, alpha3Code : String) = new Language(name, alpha3Code, None)

Предпосылки в порядке. Вы могли бы написать это так:

require(Option(alpha3Code) exists (_.nonEmpty))
require(Option(name) exists (_.nonEmpty))

Не обязательно улучшение, хотя.

A String имеет hashCode, поэтому я не понимаю, почему вы вызываете другой метод для генерации хеш-кода вместо простого вызова alpha3Code.hashCode. Я думаю, что в Scala API что-то есть. Не уверен.

В коде equals должен быть метод canEqual, если вы не указали класс sealed или final. Сопоставление с образцом - в значительной степени способ сделать это, хотя вы могли бы написать это так при наличии экстрактора:

case Language(_, `alpha3Code`, _) => true

Но то, как вы это написали, во многом так, как обычно.

4 голосов
/ 28 января 2011

Я думаю, что вы должны выставить только Option[String] в заводском методе.Например, я, как пользователь вашей библиотеки, также задам себе вопрос, какой метод фабрики мне следует использовать.И, скорее всего, я буду использовать вариант.

Scala предоставляет нам достаточно инструментов, чтобы облегчить нашу жизнь.Например, вы можете использовать значение по умолчанию для этой опции:

def apply(name: String, alpha3Code: String, alpha2Code: Option[String] = None) = 
 new Language(name, alpha3Code, alpha2Code)

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

implicit def anyToOption[T](t: T): Option[T] = Some(t)

или даже (если я лично использую нули):

implicit def anyToOption[T](t: T): Option[T] = 
 if (t == null) None else Some(t)

Но я считаю, что если вы включите опцию, это сделает ваш API более надежнымясно.

0 голосов
/ 28 января 2011

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

Параметры по умолчанию или отдельные перегрузки часто являются лучшим вариантом.Поэтому я переписал бы ваш код следующим образом:

package com.sirika.openplacesearch.api.language

import com.google.common.base.Strings
import com.google.common.base.Objects

object Language {
    def apply(name : String, alpha3Code : String, alpha2Code : String) = new Language(name, alpha3Code, alpha2Code)
    def apply(name : String, alpha3Code : String ) = new Language(name, alpha3Code)
    def unapply(l : Language) = Some(l.name, l.alpha3Code, l.alpha2Code )
}


class Language private (val name : String, val alpha3Code : String, val alpha2Code : Option[String]) {
    def this(name:String,alpha3Code: String ,alpha2Code:String) = this(name,alpha3Code,Option(alpha2Code))
    def this(name:String,alpha3Code: String) = this(name,alpha3Code,None)

    require(!Strings.isNullOrEmpty(alpha3Code))
    require(!Strings.isNullOrEmpty(name))

    override def hashCode  = alpha3Code.hashCode

    override def equals(other: Any) = other match {
        case that: Language => this.alpha3Code == that.alpha3Code
        case _ => false
    }

    override def toString = MoreObjects.toStringHelper(this)
        .add("name", name)    
        .add("alpha3", alpha3Code)
        .add("alpha2", alpha2Code)
        .toString()
}

Документы Guava

...