0Pricing
Scala for Backend Engineering & Functional Programming · Lektion

Modellierung mit ADTs

Möglicherweise ungültige Zustände nicht darstellbar machen

Modellierung mit ADTs ist eine kostenlose Scala for Backend Engineering & Functional Programming-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Scala for Backend Engineering & Functional Programming-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Scala for Backend Engineering & Functional Programming-Kurs umfasst insgesamt 4 Lektionen.

Algebraische Datentypen

Algebraische Datentypen (ADTs) bilden die Grundlage der funktionalen Domänenmodellierung. Sie kombinieren Produkttypen (UND) und Summentypen (ODER), um Daten präzise zu beschreiben.

  • Produkt: ein Datensatz mit mehreren Feldern.
  • Summe: eine Auswahl aus mehreren Varianten.
case class Point(x: Int, y: Int) // product type

object Main:
  def main(args: Array[String]): Unit =
    println(Point(1, 2))

Produkttypen

Ein Produkttyp bündelt Werte. In Scala ist eine case class ein Produkt: Eine Instanz enthält alle ihre Felder gleichzeitig.

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)

Summentypen

Ein Summentyp ist ein Wert, der genau eine von mehreren Varianten darstellt. Scala-3-Enums drücken dies direkt aus.

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)

Ungültige Zustände nicht darstellbar machen

Das zentrale Prinzip lautet: Entwerfen Sie Typen so, dass ungültige Daten nicht einmal konstruiert werden können. Wenn das Typsystem einen fehlerhaften Zustand verbietet, benötigen Sie dafür keine Laufzeitprüfung.

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)

Schlechtes Design vs. gutes Design

Ein flacher Datensatz mit nullable Feldern begünstigt ungültige Zustände. Wenn Sie jeden Fall als Variante modellieren, verschwinden diese.

  • Schlecht: case class Conn(connected: Boolean, sessionId: String) erlaubt connected=false zusammen mit einer sessionId.
  • Gut: Ein Summentyp verknüpft das Feld mit dem richtigen Zustand.
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)

Produkte und Summen kombinieren

In realen Modellen werden Produkte in Summen und umgekehrt verschachtelt. Jede Variante kann ihr eigenes Produkt aus Feldern enthalten.

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)

Pattern Matching mit ADTs

Sie verwenden ADTs mit Pattern Matching. Da der Typ abgeschlossen ist, warnt der Compiler, wenn Sie eine Variante übersehen.

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)))

Rekursive ADTs

ADTs können auf sich selbst verweisen und dadurch Bäume, Listen und Ausdrücke auf natürliche Weise modellieren.

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))

Optionalität mit Option

Verwenden Sie Option statt null, um ein möglicherweise fehlendes Feld zu modellieren. Der Typ macht das Fehlen explizit und erzwingt dessen Behandlung.

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)"))

Größen präzise modellieren

Ersetzen Sie die Primitive Obsession. Anstatt rohe Ints und Strings zu verwenden, kapseln Sie bedeutungsvolle Größen, damit die Typen die Absicht dokumentieren und Verwechslungen verhindern.

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)

Warum ADTs wichtig sind

ADTs bieten Korrektheit und Klarheit.

  • Ungültige Zustände können nicht erstellt werden.
  • Vollständiges Pattern Matching erkennt fehlende Fälle zur Compile-Zeit.
  • Die Struktur der Daten dokumentiert die Domäne.
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)

Kurzer Check

Testen Sie Ihr Verständnis der Modellierung mit ADTs.

Zusammenfassung

Sie haben die Domänenmodellierung mit ADTs kennengelernt.

  • Produkttypen (case class) kombinieren Felder mit UND.
  • Summentypen (enum) bieten eine Auswahl mit ODER.
  • Verschachteln Sie beide, um umfangreiche Domänen und rekursive Strukturen zu modellieren.
  • Verwenden Sie Option statt null.
  • Entwerfen Sie das Modell so, dass ungültige Zustände nicht darstellbar sind.
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")))

Häufig gestellte Fragen

Ist die Lektion „Modellierung mit ADTs“ kostenlos?

Ja — der vollständige Text von „Modellierung mit ADTs“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Scala for Backend Engineering & Functional Programming-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Scala for Backend Engineering & Functional Programming-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Modellierung mit ADTs“?

Möglicherweise ungültige Zustände nicht darstellbar machen Du übst Scala for Backend Engineering & Functional Programming mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Scala for Backend Engineering & Functional Programming zu starten?

Keine Vorkenntnisse erforderlich. Scala for Backend Engineering & Functional Programming auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.

Wie lange dauert die Lektion „Modellierung mit ADTs“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Scala for Backend Engineering & Functional Programming-Lektion Code schreiben und ausführen?

Ja. Jede Scala for Backend Engineering & Functional Programming-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Modellierung mit ADTs
  2. Intelligente Konstruktoren
  3. Newtypes
  4. Domänen kombinieren
← Zurück zu Scala for Backend Engineering & Functional Programming