Scala for Backend Engineering & Functional Programming · Урок

Составные предметные области

Создавайте более крупные модели

Урок 4 из 413 шагов

«Составные предметные области» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.

Создание более крупных моделей

Предметные области строятся из небольших частей. Композиция предметных областей означает объединение хорошо смоделированных ADT, новых типов и проверенных значений в более крупные, но по-прежнему корректные структуры.

  • Небольшие специализированные типы объединяются в более крупные.
  • Каждая часть сохраняет свои инварианты.
case class Money(cents: Long)
case class Product(name: String, price: Money)

object Main:
  def main(args: Array[String]): Unit =
    println(Product("Book", Money(1999)))

Вложенные произведения

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

case class Address(city: String, zip: String)
case class Customer(name: String, address: Address)

object Main:
  def main(args: Array[String]): Unit =
    val c = Customer("Ada", Address("London", "E1"))
    println(c.address.city)

Композиция с коллекциями

Используйте List, Map и Set, чтобы моделировать отношения «один ко многим» внутри предметной области.

case class Item(name: String, qty: Int)
case class Order(id: Int, items: List[Item])

object Main:
  def main(args: Array[String]): Unit =
    val order = Order(1, List(Item("pen", 2), Item("ink", 1)))
    println(order.items.map(_.qty).sum)

Композиция типов-сумм

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

enum Payment:
  case Card(last4: String)
  case Cash

enum Checkout:
  case Completed(payment: Payment)
  case Abandoned

object Main:
  def main(args: Array[String]): Unit =
    val c: Checkout = Checkout.Completed(Payment.Card("1234"))
    println(c)

Функции, составляющие предметные области

Операции предметной области — это чистые функции, преобразующие типы входных данных в типы выходных данных. Их композиция образует рабочие процессы.

case class Cart(total: Long)
case class Receipt(total: Long, tax: Long)

object Main:
  def checkout(c: Cart): Receipt =
    Receipt(c.total, (c.total * 0.2).toLong)

  def main(args: Array[String]): Unit =
    println(checkout(Cart(1000)))

Композиция проверок

Если каждое поле создаётся с помощью интеллектуального конструктора, объединяйте их в выражении for. Весь объект будет создан только в том случае, если проверку пройдёт каждая его часть.

object V:
  def name(s: String): Option[String] = if s.nonEmpty then Some(s) else None
  def age(n: Int): Option[Int] = if n >= 0 then Some(n) else None

case class Person(name: String, age: Int)

object Main:
  def make(n: String, a: Int): Option[Person] =
    for nm <- V.name(n); ag <- V.age(a) yield Person(nm, ag)

  def main(args: Array[String]): Unit =
    println(Main.make("Bob", 30))
    println(Main.make("", 30))

Свёртка составных данных

Агрегируйте составные структуры с помощью fold или комбинации map и sum. Форма данных подсказывает, как выполнять вычисление.

case class Line(price: Long, qty: Int)
case class Invoice(lines: List[Line])

object Main:
  def total(inv: Invoice): Long =
    inv.lines.foldLeft(0L)((acc, l) => acc + l.price * l.qty)

  def main(args: Array[String]): Unit =
    val inv = Invoice(List(Line(100, 2), Line(50, 4)))
    println(total(inv))

Обновление неизменяемых моделей

Составляйте изменения с помощью copy: этот метод возвращает новое значение, в котором указанные поля заменены. Исходное значение остаётся без изменений.

case class Account(id: Int, balance: Long)

object Main:
  def deposit(a: Account, amt: Long): Account =
    a.copy(balance = a.balance + amt)

  def main(args: Array[String]): Unit =
    val a = Account(1, 100)
    println(deposit(a, 50))

Моделирование конечного автомата

Сочетайте типы-суммы и функции переходов, чтобы смоделировать конечный автомат, в котором существуют только допустимые переходы.

enum Light:
  case Red, Green, Yellow

object Main:
  def next(l: Light): Light = l match
    case Light.Red    => Light.Green
    case Light.Green  => Light.Yellow
    case Light.Yellow => Light.Red

  def main(args: Array[String]): Unit =
    println(next(Light.Red))
    println(next(Light.Green))

Разделение предметной области и операций на уровни

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

case class Temp(celsius: Double):
  def toF: Double = celsius * 9 / 5 + 32

object Main:
  def main(args: Array[String]): Unit =
    val t = Temp(25.0)
    println(t.toF)

Объединение всех частей

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

  • Сначала создавайте небольшие типы.
  • Объединяйте их в агрегаты.
  • Представляйте операции в виде чистых функций.
case class Sku(value: String)
case class Line(sku: Sku, qty: Int)
case class Basket(lines: List[Line]):
  def count: Int = lines.map(_.qty).sum

object Main:
  def main(args: Array[String]): Unit =
    val b = Basket(List(Line(Sku("A"), 2), Line(Sku("B"), 3)))
    println(b.count)

Быстрая проверка

Проверьте, насколько хорошо Вы поняли композицию предметных областей.

Итоги

Вы научились составлять предметные области.

  • Вкладывайте произведения и суммы, чтобы моделировать сложные структуры.
  • Используйте коллекции для отношений «один ко многим».
  • Составляйте проверки с помощью выражений for.
  • Обновляйте значения неизменяемым способом с помощью copy.
  • Оставляйте данные чистыми, а операции выражайте функциями.
case class User(id: Int, name: String)
case class Post(author: User, text: String)

object Main:
  def main(args: Array[String]): Unit =
    val p = Post(User(1, "Ada"), "hello")
    println(p.copy(text = "hi"))
Можно начать бесплатно

Изучай 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 структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Составные предметные области»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?

Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Моделирование с помощью ADT
  2. Умные конструкторы
  3. Newtype-типы
  4. Составные предметные области
← Назад к Scala for Backend Engineering & Functional Programming