קומפוזיציה לעומת ירושה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GDScript של Coddy. שיעור 40 מתוך 65.
מחלקות משתמשות מחדש בקוד בשתי דרכים. ירושה (extends) פירושה שמחלקה היא סוג של מחלקה אחרת: Mage היא Player. הרכבה פירושה שלמחלקה יש רכיב: היא מאחסנת אובייקט אחר במשתנה חבר ומעבירה אליו את העבודה:
# motor.gd
class_name Motor
extends RefCounted
func start() -> String:
return "motor running"
# car.gd
class_name Car
extends RefCounted
var motor := Motor.new()
func start() -> String:
return "Car starts, " + motor.start()Car.new().start() מחזיר Car starts, motor running. המכונית לא יודעת איך מנוע עובד: היא קוראת ל־motor.start(). העברת קריאה לחלק בצורה כזאת נקראת האצלה. (שם המחלקה הוא Motor כי Engine היא כבר מחלקה של Godot.)
קומפוזיציה משתלמת כשהחלקים משתנים או מתחברים. בירושה, דמות שהולכת ודמות שעפה הן שתי מחלקות משנה, ועטלף שמתחיל לעוף לא יכול לשנות את המחלקה שלו. בקומפוזיציה, לדמות יש חלק שאחראי לתנועה, והחלפתו מתבצעת בהשמה אחת:
# character.gd
class_name Character
extends RefCounted
var char_name: String
var movement: Movement
func _init(new_name: String, new_movement: Movement) -> void:
char_name = new_name
movement = new_movement
func move() -> String:
return char_name + " " + movement.move()
# main.gd
var bat := Character.new("Bat", Walk.new())
print(bat.move()) # Bat הולך
bat.movement = Fly.new()
print(bat.move()) # Bat עףכאן Walk ו־Fly מרחיבים מחלקת Movement קטנה: שתי הטכניקות פועלות יחד. מחלקת GDScript יכולה לבצע extend למחלקה אחת בלבד, ולכן הרכבה היא גם הדרך שבה אובייקט אחד מקבל כמה יכולות, על ידי החזקה בכמה חלקים.
השתמשו בירושה עבור קשר אמיתי ובלתי משתנה של "הוא סוג של". השתמשו בהרכבה כאשר למחלקה "יש" משהו, או כאשר צריך להחליף את החלק בזמן שהמשחק רץ.
אתגר
קלאביר לא יורש מנשק: לאביר יש נשק, והוא יכול להחליף אותו. בנו את שתי המחלקות.
Weaponמאחסנת אתweapon_nameואתdamage, ושניהם נקבעים על ידי הבנאי:Weapon.new("Sword", 7).Knightמאחסנת אתknight_name(מהבנאי) ונשק, שערכוnullבהתחלה.equip(new_weapon)מחליפה את הנשק, ו-drop()מחזירה אותו לערךnull.attack()מחזירה אתAda attacks with Sword for 7 damage, תוך שימוש בשם האביר ובשם הנשק ובנזק שלו, או אתAda has no weaponכשאין נשק.
הקוד שסופק קורא את שם האביר ואת מספר הפקודות שאחריו (equip Sword 7, drop, attack) ומריץ אותן. עבור Ada ו-attack, equip Sword 7, attack, equip Axe 12, attack, drop, attack הוא מדפיס:
Ada has no weapon
Equipped Sword
Ada attacks with Sword for 7 damage
Equipped Axe
Ada attacks with Axe for 12 damage
Dropped the weapon
Ada has no weaponהקוד שלך שייך לקבצים weapon.gd ו-knight.gd. הקובץ main.gd מכיל את קוד הקלט/פלט שסופק, ואי אפשר לערוך אותו.
נסו בעצמכם
extends Node
func _ready():
# קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
var knight := Knight.new(OS.read_string_from_stdin())
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "equip":
knight.equip(Weapon.new(parts[1], int(parts[2])))
print("Equipped ", parts[1])
elif parts[0] == "drop":
knight.drop()
print("Dropped the weapon")
else:
print(knight.attack())
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםמילת המפתח selfמתודותמאפייניםבנאי (_init)חזרה – מחשבון פשוט2מאפייני מחלקה
משתני מופע לעומת משתנים סטטייםפונקציות סטטיותקובעי מאפייניםמאחזרי מאפייניםפרטיים לפי מוסכמהסיכום – מנהל חשבון בנק5כימוס
איברים ציבוריים ופרטייםאימות באמצעות Settersהסתרת מידעמאפיינים לקריאה בלבדסיכום – מערכת לניהול רשומות תלמידים8מושגי OOP מתקדמים
קומפוזיציה לעומת ירושהמחלקות פנימיותצמתים כרכיביםRefCounted לעומת NodeEnum במחלקותתרגלו בעצמכם: קומפיילר GDScript אונליין