Modelowanie za pomocą ADT
Spraw, aby niepoprawnych stanów nie dało się reprezentować
Modelowanie za pomocą ADT to bezpłatna lekcja Scala for Backend Engineering & Functional Programming na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Scala for Backend Engineering & Functional Programming, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.
Algebraiczne typy danych
Algebraiczne typy danych (ADT) stanowią podstawę funkcyjnego modelowania domeny. Łączą typy iloczynu (AND) i typy sumy (OR), aby precyzyjnie opisywać dane.
- Iloczyn: rekord z kilkoma polami.
- Suma: wybór spośród kilku wariantów.
case class Point(x: Int, y: Int) // product type
object Main:
def main(args: Array[String]): Unit =
println(Point(1, 2))Typy iloczynu
Typ iloczynu grupuje wartości. W Scali case class jest typem iloczynu: instancja przechowuje jednocześnie wszystkie swoje pola.
case class User(name: String, age: Int)
object Main:
def main(args: Array[String]): Unit =
val u = User("Ada", 36)
println(u.name)
println(u.age)Typy sumy
Typ sumy to wartość będąca dokładnie jednym z kilku wariantów. Enumy w Scala 3 wyrażają to bezpośrednio.
enum PaymentMethod:
case Cash
case Card(number: String)
case Crypto(wallet: String)
object Main:
def main(args: Array[String]): Unit =
val p: PaymentMethod = PaymentMethod.Card("1234")
println(p)Spraw, by niepoprawne stany były niemożliwe do przedstawienia
Podstawowa zasada brzmi: projektuj typy tak, aby niepoprawnych danych nie dało się nawet skonstruować. Jeśli system typów uniemożliwia błędny stan, nie trzeba sprawdzać go w czasie działania.
enum Connection:
case Disconnected
case Connected(sessionId: String)
object Main:
def main(args: Array[String]): Unit =
// No way to have a sessionId while Disconnected
val c: Connection = Connection.Connected("abc")
println(c)Zły projekt a dobry projekt
Płaski rekord z polami dopuszczającymi wartość null sprzyja powstawaniu niepoprawnych stanów. Zamodelowanie każdego przypadku jako wariantu usuwa te stany.
- Źle:
case class Conn(connected: Boolean, sessionId: String)pozwala na connected=false z ustawionym sessionId. - Dobrze: typ sumy wiąże pole z właściwym stanem.
enum Door:
case Open
case Closed
case Locked(key: String)
object Main:
def main(args: Array[String]): Unit =
val d: Door = Door.Locked("k1")
println(d)Łączenie iloczynów i sum
Rzeczywiste modele zagnieżdżają typy iloczynu w typach sumy i odwrotnie. Każdy wariant może zawierać własny iloczyn pól.
case class Address(city: String, zip: String)
enum Contact:
case Email(value: String)
case Postal(address: Address)
object Main:
def main(args: Array[String]): Unit =
val c: Contact = Contact.Postal(Address("Paris", "75001"))
println(c)Dopasowywanie wzorców dla ADT
ADT obsługuje się za pomocą dopasowywania wzorców. Ponieważ typ jest zamknięty, kompilator ostrzega o pominięciu wariantu.
enum Shape:
case Circle(r: Double)
case Rect(w: Double, h: Double)
object Main:
def area(s: Shape): Double = s match
case Shape.Circle(r) => 3.14159 * r * r
case Shape.Rect(w, h) => w * h
def main(args: Array[String]): Unit =
println(area(Shape.Rect(2, 3)))Rekurencyjne ADT
ADT mogą odwoływać się do samych siebie, dzięki czemu naturalnie modelują drzewa, listy i wyrażenia.
enum Expr:
case Num(value: Int)
case Add(left: Expr, right: Expr)
object Main:
def eval(e: Expr): Int = e match
case Expr.Num(v) => v
case Expr.Add(l, r) => eval(l) + eval(r)
def main(args: Array[String]): Unit =
val e = Expr.Add(Expr.Num(2), Expr.Num(3))
println(eval(e))Opcjonalność za pomocą Option
Zamiast null używaj Option do modelowania pola, którego może nie być. Typ jawnie wskazuje brak wartości i wymusza jego obsługę.
case class Profile(name: String, nickname: Option[String])
object Main:
def main(args: Array[String]): Unit =
val p = Profile("Grace", None)
println(p.nickname.getOrElse("(none)"))Precyzyjne modelowanie wielkości
Zastąp obsesję prymitywami. Zamiast surowych wartości Int i String opakowuj znaczące wielkości, aby typy dokumentowały intencję i zapobiegały pomyłkom.
case class Quantity(value: Int)
case class Price(cents: Long)
case class LineItem(qty: Quantity, price: Price)
object Main:
def main(args: Array[String]): Unit =
val item = LineItem(Quantity(3), Price(500))
println(item)Dlaczego ADT są ważne
ADT zapewniają poprawność i przejrzystość.
- Niepoprawnych stanów nie da się utworzyć.
- Kompletne dopasowanie wykrywa pominięte przypadki w czasie kompilacji.
- Struktura danych dokumentuje domenę.
enum OrderStatus:
case Pending
case Shipped(tracking: String)
case Delivered(at: Long)
object Main:
def main(args: Array[String]): Unit =
val s: OrderStatus = OrderStatus.Shipped("TRK1")
println(s)Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat modelowania opartego na ADT.
Podsumowanie
Poznał(a) Pan/Pani modelowanie domeny za pomocą ADT.
- Typy iloczynu (
case class) łączą pola operacją AND. - Typy sumy (
enum) oferują wybór za pomocą OR. - Zagnieżdżaj je, aby modelować złożone domeny i struktury rekurencyjne.
- Używaj
Optionzamiast null. - Projektuj typy tak, aby niepoprawnych stanów nie dało się reprezentować.
enum Event:
case Created(id: Int)
case Deleted(id: Int, reason: String)
object Main:
def describe(e: Event): String = e match
case Event.Created(id) => s"created $id"
case Event.Deleted(id, reason) => s"deleted $id: $reason"
def main(args: Array[String]): Unit =
println(describe(Event.Deleted(1, "spam")))Ucz się Scala dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 39
- Lekcje
- 143
Często zadawane pytania
Czy lekcja „Modelowanie za pomocą ADT” jest bezpłatna?
Tak — pełny tekst „Modelowanie za pomocą ADT” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Scala for Backend Engineering & Functional Programming, przejdź na CoddyKit PRO. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.
Co nauczysz się w „Modelowanie za pomocą ADT”?
Spraw, aby niepoprawnych stanów nie dało się reprezentować Ćwiczysz Scala for Backend Engineering & Functional Programming z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Scala for Backend Engineering & Functional Programming?
Nie wymagamy żadnego doświadczenia. Scala for Backend Engineering & Functional Programming w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Modelowanie za pomocą ADT”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Scala for Backend Engineering & Functional Programming?
Tak. Każda lekcja Scala for Backend Engineering & Functional Programming zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Modelowanie za pomocą ADT
- Inteligentne konstruktory
- Newtype
- Komponowanie domen