Почему и как перенести класс case в экстрактор в Scala - PullRequest
3 голосов
/ 05 июня 2019

В Программирование в Scala обсуждение того, когда использовать классы случаев и когда экстракторы, они говорят,

Экстракторы разрывают связь между представлениями данных и шаблонами ... Это свойство называется независимость представления ...

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

К счастью, вам не нужно сразу принимать решение. Вы всегда можете начать с case-классов, а затем, если возникнет такая необходимость, перейти на экстракторы. Поскольку шаблоны в экстракторах и шаблоны в классах дел в Scala выглядят одинаково, сопоставления с образцами в ваших клиентах продолжат работать.

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

case class Email(user: String, domain: String)

и клиент Боб, и я, и играем против нее, как

def send(addr: Email) = addr match {
  case Email(user, domain) => ...
}

send(Email("joel", "gmail.com"))

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

// case class Email(user: String, domain: String)

object Email {
  def apply(user: String, domain: String) = user + "@" + domain

  def unapply(addr: String) = addr split "@" match {
    case Array(user: String, domain: String) => Some(user, domain)
    case _ => None
  }
}

это все работает, за исключением ошибки компиляции в send: "not found: type Email". Я могу это исправить с помощью

type Email = String

но это правильный путь? И я не совсем понимаю, как это помогло мне получить независимость представительства. Конечно, Боб может теперь совпадать с "joel@gmail.com" или Email("joel", "gmail.com") (которые теперь эквивалентны), но я должен предположить, что они используют Email s.

1 Ответ

0 голосов
/ 06 июня 2019

Я предполагаю, что независимость представления означала, что вы можете хранить вещи внутри Email любым способом, который вам нужен, но вам все равно нужен этот отдельный тип Email ... если вы хотите вернутьEmail из apply и извлечение из него в unapply:

class Email(private val internal: String)

object Email {
  def apply(user: String, domain: String): Email = new Email(user + "@" + domain)

  def unapply(email: Email): Option[(String, String)] =
    email.internal split "@" match {
      case Array(user: String, domain: String) => Some((user, domain))
      case _                                   => None
    }
}

Таким образом, вы можете написать:

val email: Email = Email("my", "email.com")

email match {
  case Email(user, domain) => ...
}

при возможности изменения внутренних элементов Emailчто вам нравится.Но все же, должен быть какой-то тип Email, и будет ли он trait, class и что внутри, не должно волновать пользователя вашего API.Вы также можете написать type Email = String, но псевдоним типа не является «новым типом», поэтому вы можете поместить туда любой String, и компилятор не будет соответствовать.Хотите вы этого или нет, решать вам.

...