Умные конструкторы
Проверяемое создание
«Умные конструкторы» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Создание с проверкой
Умный конструктор — это фабрика, которая проверяет входные данные перед созданием значения. Он гарантирует, что любой имеющийся у Вас экземпляр удовлетворяет инвариантам типа.
- Необработанный конструктор скрыт.
- Публично доступно только создание с проверкой.
class Age private (val value: Int)
object Age:
def of(v: Int): Option[Age] =
if v >= 0 then Some(new Age(v)) else None
object Main:
def main(args: Array[String]): Unit =
println(Age.of(30).map(_.value))Закрытый конструктор
Пометка основного конструктора как private не позволяет вызывающим обойти проверку. Сопутствующий объект становится единой точкой входа.
class Percentage private (val value: Int)
object Percentage:
def of(v: Int): Option[Percentage] =
if v >= 0 && v <= 100 then Some(new Percentage(v)) else None
object Main:
def main(args: Array[String]): Unit =
println(Percentage.of(50).map(_.value))
println(Percentage.of(150))Возврат Option
Возврат Option показывает, что создание может завершиться неудачей. Вызывающий код должен обработать None, поэтому недопустимые данные не проходят незаметно.
class NonEmpty private (val value: String)
object NonEmpty:
def of(s: String): Option[NonEmpty] =
if s.nonEmpty then Some(new NonEmpty(s)) else None
object Main:
def main(args: Array[String]): Unit =
println(NonEmpty.of("").map(_.value))
println(NonEmpty.of("hi").map(_.value))Возврат Either с подробностями ошибки
Если нужно объяснить, почему проверка не пройдена, возвращайте Either[Error, T]. Левая часть содержит описательное сообщение.
class Username private (val value: String)
object Username:
def of(s: String): Either[String, Username] =
if s.isEmpty then Left("empty")
else if s.length > 10 then Left("too long")
else Right(new Username(s))
object Main:
def main(args: Array[String]): Unit =
println(Username.of(""))
println(Username.of("ada").map(_.value))Умные конструкторы с case class
case class с закрытым конструктором также подходит. Учтите, что сгенерированные apply и copy тоже нужно контролировать, поэтому определите собственную фабрику и оставьте конструктор закрытым.
case class Email private (value: String)
object Email:
def of(s: String): Option[Email] =
if s.contains("@") then Some(Email(s)) else None
object Main:
def main(args: Array[String]): Unit =
println(Email.of("a@b.com"))
println(Email.of("bad"))Объединение проверенных значений
Поскольку умные конструкторы возвращают Option или Either, их можно объединять с помощью выражений for для создания более крупных проверенных объектов.
case class Name private (value: String)
object Name:
def of(s: String): Option[Name] =
if s.nonEmpty then Some(Name(s)) else None
case class Person(name: Name, age: Int)
object Main:
def make(n: String): Option[Person] =
for nm <- Name.of(n) yield Person(nm, 20)
def main(args: Array[String]): Unit =
println(Main.make("Bob"))Нормализация входных данных
Умный конструктор также может нормализовать данные, например удалять пробелы по краям или переводить текст в нижний регистр, чтобы все экземпляры имели каноническую форму.
case class Tag private (value: String)
object Tag:
def of(s: String): Option[Tag] =
val clean = s.trim.toLowerCase
if clean.nonEmpty then Some(Tag(clean)) else None
object Main:
def main(args: Array[String]): Unit =
println(Tag.of(" Scala "))Инварианты сохраняются навсегда
После того как значение прошло умный конструктор, его инвариант гарантирован на протяжении всего времени существования. Последующий код может доверять значению и не проверять его повторно.
case class PositiveInt private (value: Int)
object PositiveInt:
def of(n: Int): Option[PositiveInt] =
if n > 0 then Some(PositiveInt(n)) else None
object Main:
def doubleIt(p: PositiveInt): Int = p.value * 2 // always positive
def main(args: Array[String]): Unit =
PositiveInt.of(5).foreach(p => println(Main.doubleIt(p)))Сочетание с непрозрачными типами
Чтобы получить проверенные значения без затрат, сочетайте умный конструктор с непрозрачным типом. Объект-обёртка не выделяется, но проверка по-прежнему контролирует создание.
object Domain:
opaque type Score = Int
def of(n: Int): Option[Score] =
if n >= 0 && n <= 100 then Some(n) else None
extension (s: Score) def value: Int = s
object Main:
def main(args: Array[String]): Unit =
println(Domain.of(88).map(_.value))Несколько правил проверки
Применяйте несколько проверок последовательно. Первое неудачное правило останавливает дальнейшую обработку и возвращает понятную ошибку.
object Password:
def of(s: String): Either[String, String] =
if s.length < 8 then Left("too short")
else if !s.exists(_.isDigit) then Left("need a digit")
else Right(s)
object Main:
def main(args: Array[String]): Unit =
println(Password.of("abc"))
println(Password.of("abcdef12"))Когда использовать умные конструкторы
Используйте их всегда, когда инварианты типа могут быть нарушены при необработанном создании.
- Ограниченные числа, непустые строки, идентификаторы заданного формата.
- Возвращайте
Optionдля простого ответа да/нет иEitherдля указания причины. - Оставляйте необработанный конструктор закрытым.
case class Port private (value: Int)
object Port:
def of(n: Int): Option[Port] =
if n >= 1 && n <= 65535 then Some(Port(n)) else None
object Main:
def main(args: Array[String]): Unit =
println(Port.of(8080))
println(Port.of(70000))Быстрая проверка
Проверьте, насколько хорошо Вы поняли умные конструкторы.
Итоги
Вы изучили умные конструкторы.
- Проверяйте входные данные в фабрике сопутствующего объекта.
- Сделайте необработанный конструктор закрытым.
- Возвращайте
OptionилиEither, чтобы заставить обработать ошибку. - При необходимости нормализуйте входные данные до канонической формы.
- После этого инварианты сохраняются на протяжении всего времени существования значения.
case class Even private (value: Int)
object Even:
def of(n: Int): Option[Even] =
if n % 2 == 0 then Some(Even(n)) else None
object Main:
def main(args: Array[String]): Unit =
println(Even.of(4))
println(Even.of(5))Изучай Scala с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 39
- Уроки
- 143
Часто задаваемые вопросы
Урок «Умные конструкторы» бесплатный?
Да — полный текст урока «Умные конструкторы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Чему я научусь в уроке «Умные конструкторы»?
Проверяемое создание Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?
Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Умные конструкторы»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?
Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Моделирование с помощью ADT
- Умные конструкторы
- Newtype-типы
- Составные предметные области