מחלקות ומופעים של אובייקטים ב-Kotlin

במהלך ה-codelabs האלה, תיצרו אפליקציית קובייה מתגלגלת ל-Android. כשמשתמשים "מגלגלים את הקובייה", נוצרת תוצאה אקראית. התוצאה מתחשבת במספר הצדדים של הקובייה. לדוגמה, אפשר להטיל קובייה בעלת 6 צדדים רק עם ערכים מ-1 עד 6.

כך תיראה האפליקציה הסופית.

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

ב-codelab הראשון הזה, תיצרו תוכנית Kotlin שמדמה הטלת קובייה ומציגה מספר אקראי, בדיוק כמו קובייה.

דרישות מוקדמות

  • איך פותחים, עורכים ומריצים קוד בכתובת https://try.kotlinlang.org/
  • ליצור ולהריץ תוכנית Kotlin שמשתמשת במשתנים ובפונקציות, ומדפיסה תוצאה במסוף.
  • עיצוב מספרים בתוך טקסט באמצעות תבנית מחרוזת עם הסימון ${variable}.

מה תלמדו

  • איך ליצור מספרים אקראיים באופן אוטומטי כדי לדמות הטלות קוביות.
  • איך יוצרים מבנה לקוד באמצעות יצירת מחלקה Dice עם משתנה ושיטה.
  • איך יוצרים מופע אובייקט של מחלקה, משנים את המשתנים שלו ומפעילים את השיטות שלו.

מה תפַתחו

  • תוכנית Kotlin בכלי לתכנות Kotlin מבוסס-דפדפן שיכול לבצע הטלת קובייה אקראית.

מה צריך

  • מחשב עם חיבור לאינטרנט

לעתים קרובות יש במשחקים אלמנט אקראי. יכול להיות שתזכו בפרס אקראי או שתתקדמו במספר אקראי של צעדים בלוח המשחק. בחיים היום-יומיים, אתם יכולים להשתמש במספרים ובאותיות אקראיים כדי ליצור סיסמאות בטוחות יותר.

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

הגדרת קוד התחלתי

  1. בדפדפן, פותחים את האתר https://try.kotlinlang.org/.
  2. מוחקים את כל הקוד הקיים בעורך הקוד ומחליפים אותו בקוד שבהמשך. זו הפונקציה main() שעבדתם איתה ב-codelabs קודמים (ראו את ה-codelab Write your first Kotlin program).
fun main() {

}

שימוש בפונקציה random

כדי לגלגל קובייה, צריך דרך לייצג את כל הערכים האפשריים של גלגול הקובייה. בקוביות רגילות עם 6 צדדים, התוצאות המקובלות הן: 1, 2, 3, 4, 5 ו-6.

קודם למדנו שיש סוגים של נתונים כמו Int למספרים שלמים ו-String לטקסט. ‫IntRange הוא סוג נתונים נוסף, והוא מייצג טווח של מספרים שלמים מנקודת התחלה ועד נקודת סיום. ‫IntRange הוא סוג נתונים מתאים לייצוג הערכים האפשריים שמתקבלים בהטלת קובייה.

  1. בתוך הפונקציה main(), מגדירים משתנה כ-val בשם diceRange. מקצים לו ערך של IntRange מ-1 עד 6, שמייצג את טווח המספרים השלמים שאפשר לקבל בהטלת קובייה בעלת 6 צדדים.
val diceRange = 1..6

אפשר לדעת ש-1..6 הוא טווח ב-Kotlin כי הוא כולל מספר התחלה, שתי נקודות ואז מספר סיום (ללא רווחים ביניהם). דוגמאות נוספות לטווחים של מספרים שלמים הן 2..5 למספרים 2 עד 5, ו-100..200 למספרים 100 עד 200.

בדומה לקריאה לפונקציה println() שמורה למערכת להדפיס את הטקסט שמועבר לה, אפשר להשתמש בפונקציה שנקראת random() כדי ליצור ולהחזיר מספר אקראי בטווח נתון. כמו קודם, אפשר לאחסן את התוצאה במשתנה.

  1. בתוך main(), מגדירים משתנה כ-val שנקרא randomNumber.
  2. הגדרת הערך של randomNumber כתוצאה של קריאה לפונקציה random() בטווח diceRange, כמו בדוגמה שלמטה.
 val randomNumber = diceRange.random()

שימו לב שקוראים לפונקציה random() ב-diceRange באמצעות נקודה בין המשתנה לקריאה לפונקציה. אפשר לקרוא את זה כ'יצירת מספר אקראי מתוך diceRange'. התוצאה מאוחסנת במשתנה randomNumber.

  1. כדי לראות את המספר שנוצר באופן אקראי, משתמשים בסימון של פורמט מחרוזת (שנקרא גם 'תבנית מחרוזת') ${randomNumber} כדי להדפיס אותו, כמו בדוגמה שלמטה.
println("Random number: ${randomNumber}")

הקוד הסופי אמור להיראות כך.

fun main() {
    val diceRange = 1..6
    val randomNumber = diceRange.random()
    println("Random number: ${randomNumber}")
}
  1. מריצים את הקוד כמה פעמים. בכל פעם, הפלט אמור להיראות כמו בדוגמה שלמטה, עם מספרים אקראיים שונים.
Random number: 4

כשמטילים קוביות, הן אובייקטים אמיתיים בידיים. הקוד שכתבתם עכשיו עובד מצוין, אבל קשה לדמיין שהוא קשור לקוביות אמיתיות. כשמארגנים תוכנית כך שהיא תהיה דומה יותר לדברים שהיא מייצגת, קל יותר להבין אותה. לכן, יהיה נחמד אם יהיו קוביות פרוגרמטיות שאפשר לגלגל!

כל הקוביות פועלות באופן זהה. יש להם את אותן התכונות, כמו מספר הצדדים, והם מתנהגים באותו אופן, למשל אפשר לגלגל אותם. ב-Kotlin, אפשר ליצור תוכנית של קובייה שקובעת שלקובייה יש צדדים ושהיא יכולה להגריל מספר אקראי. התוכנית הזו נקראת class (מחלקה).

מתוך המחלקה הזו, אפשר ליצור אובייקטים של קוביות, שנקראים מופעים של אובייקטים. לדוגמה, אפשר ליצור קובייה עם 12 צדדים או קובייה עם 4 צדדים.

הגדרת סיווג של קוביות

בשלבים הבאים תגדירו מחלקה חדשה בשם Dice שתייצג קובייה שאפשר לגלגל.

  1. כדי להתחיל מחדש, מוחקים את הקוד בפונקציה main() כך שהקוד שמתקבל יהיה כמו הקוד שמוצג למטה.
fun main() {

}
  1. מתחת לפונקציה main(), מוסיפים שורה ריקה ואז מוסיפים קוד ליצירת המחלקה Dice. כמו שמוצג למטה, מתחילים במילת המפתח class, ואחריה שם הכיתה, ואז סוגריים מסולסלים פותחים וסוגרים. משאירים רווח בין הסוגריים המסולסלים כדי להזין את הקוד של הכיתה.
class Dice {

}

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

  1. בתוך המחלקה Dice מוסיפים var בשם sides למספר הצדדים של הקובייה. מגדירים את sides ל-6.
class Dice {
    var sides = 6
}

זה הכול. עכשיו יש לכם מחלקה פשוטה מאוד שמייצגת קובייה.

יצירת מופע של המחלקה Dice

עם המחלקה Dice הזו, יש לכם תוכנית של קובייה. כדי שתהיה לכם קובייה בפועל בתוכנית, אתם צריכים ליצור מופע של אובייקט Dice. (ואם הייתם צריכים שלוש קוביות, הייתם יוצרים שלוש מופעים של אובייקט).

  1. כדי ליצור מופע אובייקט של Dice, בפונקציה main(), יוצרים val בשם myFirstDice ומאתחלים אותו כמופע של המחלקה Dice. שימו לב לסוגריים אחרי שם המחלקה, שמציינים שאתם יוצרים מופע אובייקט חדש מהמחלקה.
fun main() {
    val myFirstDice = Dice()
}

עכשיו, אחרי שיש לכם אובייקט myFirstDice, כלומר דבר שנוצר מהתוכנית, אתם יכולים לגשת למאפיינים שלו. המאפיין היחיד של Dice הוא sides. כדי לגשת לנכס, משתמשים בסימון הנקודות. לכן, כדי לגשת למאפיין sides של myFirstDice, קוראים ל-myFirstDice.sides, שמבוטא 'myFirstDice נקודה sides'.

  1. מתחת להצהרה של myFirstDice, מוסיפים הצהרה של println() כדי להציג את מספר sides של myFirstDice.
println(myFirstDice.sides)

הקוד אמור להיראות כך.

fun main() {
    val myFirstDice = Dice()
    println(myFirstDice.sides)
}

class Dice {
    var sides = 6
}
  1. מריצים את התוכנית, והיא אמורה להפיק את מספר sides שמוגדר במחלקה Dice.
6

עכשיו יש לכם כיתה Dice וקוביות אמיתיות myFirstDice עם 6 sides.

בואו נטיל את הקוביות!

הטלת קוביות

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

  1. בכיתה Dice, מתחת למשתנה sides, מוסיפים שורה ריקה ואז יוצרים פונקציה חדשה להטלת הקובייה. מתחילים במילת המפתח fun של Kotlin, ואחריה שם השיטה, ואחריה סוגריים (), ואחריהם סוגריים מסולסלים פותחים וסוגרים {}. אפשר להשאיר שורה ריקה בין הסוגריים המסולסלים כדי לפנות מקום לקוד נוסף, כמו בדוגמה הבאה. הכיתה אמורה להיראות כך.
class Dice {
    var sides = 6

    fun roll() {

    }
}

כשמטילים קובייה בעלת 6 צדדים, מתקבל מספר אקראי בין 1 ל-6.

  1. בתוך השיטה roll(), יוצרים val randomNumber. מקצים לו מספר אקראי בטווח 1..6. משתמשים בסימון הנקודה כדי לקרוא ל-random() בטווח.
val randomNumber = (1..6).random()
  1. אחרי שיוצרים את המספר האקראי, מדפיסים אותו במסוף. השיטה roll() המוגמרת אמורה להיראות כמו הקוד שבהמשך.
fun roll() {
     val randomNumber = (1..6).random()
     println(randomNumber)
}
  1. כדי להטיל קובייה myFirstDice, ב-main(), קוראים ל-method‏ roll() ב-myFirstDice. מפעילים שיטה באמצעות סימון הנקודות. לכן, כדי להפעיל את השיטה roll() של myFirstDice, מקלידים myFirstDice.roll(), שמבוטא 'myFirstDice נקודה roll()'.
myFirstDice.roll()

הקוד המלא אמור להיראות כך.

fun main() {
    val myFirstDice = Dice()
    println(myFirstDice.sides)
    myFirstDice.roll()
}

class Dice {
    var sides = 6

    fun roll() {
        val randomNumber = (1..6).random()
        println(randomNumber)
    }
}
  1. מריצים את הקוד. תוצאת הטלת הקובייה האקראית תופיע מתחת למספר הצדדים. מריצים את הקוד כמה פעמים ורואים שמספר הצדדים נשאר זהה, אבל הערך של הטלת הקובייה משתנה.
6
4

מעולה! הגדרתם מחלקה Dice עם משתנה sides ופונקציה roll(). בפונקציה main() יצרתם מופע חדש של אובייקט Dice ואז קראתם למתודה roll() כדי ליצור מספר אקראי.

בשלב הזה אתם מדפיסים את הערך של randomNumber בפונקציה roll() וזה עובד מצוין. אבל לפעמים יותר שימושי להחזיר את התוצאה של פונקציה לכל מה שקרא לפונקציה. לדוגמה, אפשר להקצות את התוצאה של השיטה roll() למשתנה, ואז להזיז שחקן בסכום הזה. איך עושים את זה?

  1. בקטע main() משנים את השורה myFirstDice.roll(). יוצרים val בשם diceRoll. מגדירים אותו כערך שמוחזר על ידי השיטה roll().
val diceRoll = myFirstDice.roll()

הפעולה הזו לא עושה כלום כי roll() לא מחזירה כלום. כדי שהקוד הזה יפעל כמצופה, הפונקציה roll() צריכה להחזיר ערך כלשהו.

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

  1. משנים את הפונקציה roll() כדי לציין איזה סוג נתונים יוחזר. במקרה הזה, המספר האקראי הוא Int, ולכן סוג ההחזרה הוא Int. התחביר לציון סוג ההחזרה הוא: אחרי שם הפונקציה, אחרי הסוגריים, מוסיפים נקודתיים, רווח ואז את מילת המפתח Int של סוג ההחזרה של הפונקציה. הגדרת הפונקציה צריכה להיראות כמו הקוד שבהמשך.
fun roll(): Int {
  1. מריצים את הקוד הזה. תופיע שגיאה בתצוגת הבעיות. ההודעה:
A ‘return'  expression is required in a function with a block body. 

שינית את הגדרת הפונקציה כך שתחזיר Int, אבל המערכת מתלוננת שהפונקציה

הקוד לא מחזיר בפועל Int. 'גוף הבלוק' או 'גוף הפונקציה' מתייחסים לקוד שבין הסוגריים המסולסלים של פונקציה. כדי לתקן את השגיאה הזו, צריך להחזיר ערך מפונקציה באמצעות הצהרת return בסוף גוף הפונקציה.

  1. ב-roll(), מסירים את ההצהרה println() ומחליפים אותה בהצהרה return לגבי randomNumber. הפונקציה roll() שלכם צריכה להיראות כמו הקוד שבהמשך.
fun roll(): Int {
     val randomNumber = (1..6).random()
     return randomNumber
}
  1. ב-main() מסירים את הצהרת ההדפסה של צדי הקובייה.
  2. מוסיפים הצהרה להדפסת הערך של sides ושל diceRoll במשפט אינפורמטיבי. פונקציית main() המוגמרת אמורה להיראות כמו הקוד שבהמשך.
fun main() {
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}
  1. מריצים את הקוד והפלט אמור להיראות כך.
Your 6 sided dice rolled 4!

זה כל הקוד שלך עד עכשיו.

fun main() {
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}


class Dice {
    var sides = 6

    fun roll(): Int {
        val randomNumber = (1..6).random()
        return randomNumber
    }
}

לא לכל הקוביות יש 6 צדדים! יש קוביות מכל הצורות והגדלים: 4 צדדים, 8 צדדים, עד 120 צדדים!

  1. בשיטה roll() של המחלקה Dice, משנים את הערך הקבוע 1..6 לשימוש ב-sides, כך שהטווח, ומכאן המספר האקראי שיוגרל, תמיד יהיה נכון למספר הצדדים.
val randomNumber = (1..sides).random()
  1. בפונקציה main(), מתחת להדפסה של הטלת הקובייה ואחריה, משנים את sides של FirstDice ל-20.
myFirstDice.sides = 20
  1. מעתיקים את הצהרת ההדפסה הקיימת שבהמשך ומדביקים אותה אחרי המקום שבו שיניתם את מספר הצדדים.
  2. מחליפים את ההדפסה של diceRoll בהדפסה של התוצאה של קריאה לשיטה roll() ב-myFirstDice.
println("Your ${myFirstDice.sides} sided dice has rolled a ${myFirstDice.roll()}!")

התוכנית שלכם אמורה להיראות כך.

fun main() {
   
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")

    myFirstDice.sides = 20
    println("Your ${myFirstDice.sides} sided dice rolled ${myFirstDice.roll()}!")
}

class Dice {
    var sides = 6

    fun roll(): Int {
        val randomNumber = (1..sides).random()
        return randomNumber
    }
}
  1. מריצים את התוכנית וצריכה להופיע הודעה לגבי הקובייה בעלת 6 הצדדים, והודעה שנייה לגבי הקובייה בעלת 20 הצדדים.
Your 6 sided dice rolled 3!
Your 20 sided dice rolled 15!

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

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

  1. משנים את הגדרת המחלקה Dice כך שתקבל ארגומנט מסוג integer בשם numSides. הקוד בתוך הכיתה לא משתנה.
class Dice(val numSides: Int) {
   // Code inside does not change.
}
  1. בתוך המחלקה Dice, מוחקים את המשתנה sides, כי עכשיו אפשר להשתמש ב-numSides.
  2. בנוסף, צריך לתקן את הטווח כדי להשתמש ב-numSides.

המחלקות Dice אמורות להיראות כך.

class Dice (val numSides: Int) {

    fun roll(): Int {
        val randomNumber = (1..numSides).random()
        return randomNumber
    }
}

אם מריצים את הקוד הזה, יופיעו הרבה שגיאות, כי צריך לעדכן את main() כדי שיתאים לשינויים שבוצעו במחלקה Dice.

  1. ב-main(), כדי ליצור myFirstDice עם 6 צדדים, צריך להעביר את מספר הצדדים כארגומנט למחלקה Dice, כמו שמוצג בהמשך.
    val myFirstDice = Dice(6)
  1. במסמך להדפסה, משנים את sides ל-numSides.
  2. מתחת לזה, מוחקים את הקוד שמשנה את sides ל-20, כי המשתנה הזה כבר לא קיים.
  3. מוחקים גם את ההצהרה println שמתחתיה.

הפונקציה main() אמורה להיראות כמו הקוד שבהמשך, ואם מריצים אותה, לא אמורות להיות שגיאות.

fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
}
  1. אחרי שמדפיסים את הטלת הקובייה הראשונה, מוסיפים קוד כדי ליצור ולהדפיס אובייקט שני של Dice בשם mySecondDice עם 20 צדדים.
    val mySecondDice = Dice(20)
  1. מוסיפים הצהרת הדפסה שמגלגלת ומדפיסה את הערך המוחזר.
println("Your ${mySecondDice.numSides} sided dice rolled  ${mySecondDice.roll()}!")
  1. הפונקציה main() המוגמרת אמורה להיראות כך.
fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
    
    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        val randomNumber = (1..numSides).random()
        return randomNumber
    }
}
  1. מריצים את התוכנית המוגמרת, והפלט אמור להיראות כך.
Your 6 sided dice rolled 5!
Your 20 sided dice rolled 7!

כשכותבים קוד, עדיף לכתוב אותו בצורה תמציתית. אפשר להיפטר מהמשתנה randomNumber ולהחזיר את המספר האקראי ישירות.

  1. משנים את ההצהרה return כדי להחזיר את המספר האקראי ישירות.
    fun roll(): Int {
        return (1..numSides).random()
    }

בהדפסה השנייה של ההצהרה, אתם מכניסים את הקריאה לקבלת המספר האקראי לתבנית המחרוזת. כדי להיפטר מהמשתנה diceRoll, צריך לעשות את אותו הדבר בהצהרת ההדפסה הראשונה.

  1. קוראים ל-myFirstDice.roll() בתבנית המחרוזת ומוחקים את המשתנה diceRoll. שתי השורות הראשונות של הקוד של הפונקציה main() נראות עכשיו כך.
    val myFirstDice = Dice(6)
    println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
  1. מריצים את הקוד ולא אמור להיות הבדל בפלט.

זה הקוד הסופי אחרי שינוי המבנה שלו .

fun main() {
    val myFirstDice = Dice(6)
    println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
    
    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }
}
fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
    
    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }
}
  • מפעילים את הפונקציה random() ב-IntRange כדי ליצור מספר אקראי: (1..6).random()
  • מחלקות הן כמו תוכנית של אובייקט. יכולים להיות להם מאפיינים והתנהגויות, שמיושמים כמשתנים וכפונקציות.
  • מופע של מחלקה מייצג אובייקט, לרוב אובייקט פיזי, כמו קובייה. אפשר לקרוא לפעולות באובייקט ולשנות את המאפיינים שלו.
  • אתם יכולים להעביר קלט למחלקה כשאתם יוצרים מופע על ידי ציון ארגומנט להגדרת המחלקה. לדוגמה: class Dice(val numSides: Int) ואז יוצרים מופע עם Dice(6).
  • פונקציות יכולות להחזיר משהו. מציינים את סוג הנתונים שיוחזר בהגדרת הפונקציה, ומשתמשים בהצהרה return בגוף הפונקציה כדי להחזיר משהו. לדוגמה: fun example(): Int { return 5 }

מבצעים את הפעולות הבאות:

  • נותנים לכיתה Dice מאפיין צבע נוסף ויוצרים כמה מקרים של קוביות עם מספרים שונים של צדדים וצבעים!
  • יוצרים מחלקה Coin, נותנים לה את היכולת להפוך, יוצרים מופע של המחלקה והופכים כמה מטבעות! איך משתמשים בפונקציה random()‎ עם טווח כדי להטיל מטבע?