Menu
Coddy logo textTech

היררכיות אטומות

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Kotlin של Coddy. שיעור 34 מתוך 57.

למחלקה או לממשק sealed יש קבוצה סגורה של מחלקות משנה ישירות, שכולן מוצהרות באותה חבילה ובאותו מודול. המהדר מכיר כל אחת מהן, ולכן הן מתאימות לתוצאות שיש להן מספר קבוע של צורות:

sealed interface Result
data class Success(val value: Int) : Result
data class Failure(val reason: String) : Result

fun parseAge(text: String): Result {
    val n = text.toIntOrNull() ?: return Failure("not a number: $text")
    return if (n in 0..150) Success(n) else Failure("out of range: $n")
}

בתוך main:

println(parseAge("42"))
println(parseAge("abc"))
println(parseAge("200"))

פלט:

Success(value=42)
Failure(reason=not a number: abc)
Failure(reason=out of range: 200)

מכיוון שהקבוצה סגורה, ביטוי when על טיפוס אטום הוא ממצה גם בלי else. אם מישהו יוסיף מחלקת משנה בהמשך, כל ביטוי when כזה יפסיק להתקמפל עד שיטפל במקרה החדש, כך שאף מקרה לא יישכח:

sealed interface Result
data class Success(val value: Int) : Result
data class Failure(val reason: String) : Result

fun describe(r: Result) = when (r) {
    is Success -> "ok: ${r.value}"
    is Failure -> "error: ${r.reason}"
}

בתוך main:

println(describe(Success(7)))
println(describe(Failure("timeout")))

פלט:

ok: 7
error: timeout

מקרה ללא נתונים הוא data object: מופע יחיד עם toString() קריא. ענף ה־when שלו אינו זקוק ל־is, כי יש רק מופע אחד. המקרים האחרים יכולים להיות מחלקות נתונים, אובייקטים או מחלקות רגילות:

sealed interface Command
data object Quit : Command
data class Move(val steps: Int) : Command
data class Say(val text: String) : Command

fun parse(line: String): Command = when {
    line == "quit" -> Quit
    line.startsWith("move ") -> Move(line.drop(5).toInt())
    else -> Say(line)
}

fun run(c: Command) = when (c) {
    Quit -> "bye"
    is Move -> "moving ${c.steps}"
    is Say -> "saying ${c.text}"
}

בתוך main:

for (line in listOf("move 3", "hello", "quit")) {
    val c = parse(line)
    println("$c: ${run(c)}")
}

פלט:

Move(steps=3): moving 3
Say(text=hello): saying hello
Quit: bye

enum או היררכיה חתומה? enum הוא קבוצה קבועה של מופעים יחידים בעלי אותם מאפיינים. היררכיה חתומה מאפשרת לכל מקרה להכיל נתונים משלו, ולמקרה כמו Move יכולים להיות מופעים רבים עם ערכים שונים:

enum class Light { RED, YELLOW, GREEN }          // אותו מבנה, מופע אחד לכל ערך

sealed interface Payment                          // לכל מקרה יש נתונים משלו
data class Card(val number: String) : Payment
data class Transfer(val iban: String, val reference: String) : Payment
data object Cash : Payment
challenge icon

אתגר

קל

רובוט מבין את הפקודות של הממשק האטום Command: Move(steps), Turn(left), Report ו-Invalid(text), שמסופקות עבורך. השלימו את parse(line): move 3 נותן Move(3), turn left ו-turn right נותנים Turn, report נותן Report, וכל דבר אחר, כולל פקודת תנועה ללא מספר תקין, נותן Invalid עם השורה. לאחר מכן השלימו את Robot.execute(command) עם when ממצה וללא else. הרובוט מתחיל ב-(0, 0) ופונה לכיוון N (צפון הוא y + 1, מזרח הוא x + 1). הוא מחזיר moved to (0, 3), facing W, at (0, 3) facing W או ignored.

הקוד שסופק מנתח כל שורה ומדפיס Move(steps=3) -> moved to (0, 3).

הקוד שלכם ייכתב בקבצים Robot.kt, Parser.kt, Command.kt, Move.kt, Turn.kt, Report.kt ו-Invalid.kt. הקובץ Main.kt מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.

נסו בעצמכם

fun main() {
    // קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
    val input = generateSequence(::readLine).toList()
    val robot = Robot()
    for (line in input) {
        val command = parse(line)
        println("$command -> ${robot.execute(command)}")
    }
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: Kotlin אונליין