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
Optionstatt 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
- Modellierung mit ADTs
- Intelligente Konstruktoren
- Newtypes
- Domänen kombinieren