מיקסינים לעומת ירושה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Lua של Coddy. שיעור 53 מתוך 70.
למדת שתי דרכים לשתף התנהגות: ירושה יוצרת קשר מסוג "הוא-סוג-של" דרך שרשרת האבטיפוסים, ואילו מיקסינים מעתיקים פונקציות ישירות למחלקה. אבל מתי כדאי להשתמש בכל אחת מהגישות?
השתמשו בירושה כשלאובייקטים יש זהות בסיסית משותפת. Bird הוא Animal. הגיוני ש-Bird יירש התנהגויות בסיסיות של בעלי חיים, כמו :eat() או :sleep(). הקשר הוא קבוע ומגדיר מהו האובייקט במהותו.
השתמשו בmixins כשרוצים להוסיף יכולת שאינה מגדירה את הזהות המרכזית של האובייקט.
לא כל הציפורים יכולות לעוף: פינגווינים ויענים הם ציפורים, אבל הם לא עפים. תעופה היא יכולת, לא תכונה שמגדירה ציפור. מיקסין Flyable מאפשר להוסיף יכולת תעופה לציפורים שזקוקות לה, בלי לכפות אותה על כולן.
שתי הגישות עובדות נהדר יחד:
-- Bird יורשת מ-Animal (יחס של "הוא סוג של")
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable היא יכולת (mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- החילו את ה-mixin רק על ציפורים שיכולות לעוף
applyMixin(Bird, Flyable)התבנית הזו נותנת לך את הטוב שבשני העולמות: היררכיית מחלקות ברורה לזהות משותפת, לצד יכולות גמישות שאפשר לשלב לפי הצורך.
אתגר
קלבואו נבנה מערכת אקולוגית קטנה של יצורים שמדגימה מתי להשתמש בירושה לעומת מיקסינים! תיצרו מחלקת בסיס Animal, מחלקה Bird שיורשת ממנה, ומיקסין Swimmable, כי אף על פי שכל הציפורים הן בעלי חיים, לא כל הציפורים יכולות לשחות.
תארגנו את הקוד שלכם בארבעה קבצים:
Animal.lua: צרו את מחלקת הבסיס Animal עם בנאי:new(name)ששומר את השם. כללו מתודה:eat()שמדפיסה{name} is eating. זה מייצג את הזהות הבסיסית המשותפת לכל בעלי החיים.Swimmable.lua: צרו מיקסין עם מתודה:swim()שמדפיסה{self.name} is swimming. זוהי יכולת, ולא כל היצורים יכולים לשחות, לכן היא לא אמורה להיות חלק משרשרת הירושה.Bird.lua: צרו מחלקת Bird שיורשת מ-Animal. הבנאי:new(name, canSwim)צריך לקרוא לבנאי של מחלקת האב ולשמור אם הציפור הזאת יכולה לשחות. החילו את המיקסין Swimmable על מחלקת Bird. כללו מתודה:chirp()שמדפיסה{name} chirps!. זוהי התנהגות ייחודית לציפורים.main.lua: כתבו כאן את פונקציית העזרapplyMixin. קראו את הקלטים עבור שם הציפור ואם היא יכולה לשחות (yesאוno). צרו מופע של Bird, ואז הדגימו את היכולות שלו: קראו ל-:eat()(שעוברת בירושה מ-Animal), קראו ל-:chirp()(המתודה של Bird), וקראו ל-:swim()רק אם הציפור יכולה לשחות.
תקבלו שני קלטים:
- שם הציפור (מחרוזת)
- האם הציפור יכולה לשחות:
yesאוno
אם הציפור יכולה לשחות, הפלט שלכם צריך להיות:
{name} is eating
{name} chirps!
{name} is swimmingאם הציפור לא יכולה לשחות, הפלט שלכם צריך להיות:
{name} is eating
{name} chirps!לדוגמה, אם הקלטים הם Duck ו-yes, הפלט צריך להיות:
Duck is eating
Duck chirps!
Duck is swimmingאם הקלטים הם Sparrow ו-no, הפלט צריך להיות:
Sparrow is eating
Sparrow chirps!האתגר הזה מדגים את ההבחנה העיקרית: Bird היא Animal (ירושה), אבל שחייה היא יכולת אופציונלית (מיקסין). המיקסין מוחל על המחלקה, אבל הלוגיקה הראשית שלכם מחליטה אם להשתמש בו בהתאם ליכולות של הציפור המסוימת!
נסו בעצמכם
-- main.lua: נקודת כניסה - צרו ציפור והדגימו את יכולותיה
-- פונקציית עזר להחלת mixin על מחלקה
local function applyMixin(class, mixin)
-- TODO: העתיקו את כל המתודות מ-mixin למחלקה
end
-- החילו את ה-mixin Swimmable על Bird לפני טעינת Bird באמצעות require
-- הערה: ייתכן שתצטרכו לטפל בכך ב-Bird.lua או כאן
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- החילו את ה-mixin על המחלקה Bird
applyMixin(Bird, Swimmable)
-- קראו את הקלטים
local name = io.read()
local canSwimInput = io.read()
-- TODO: קבעו אם הציפור יכולה לשחות לפי הקלט ("yes" או "no")
-- TODO: צרו מופע של Bird עם השם ויכולת השחייה
-- TODO: קראו ל-eat() - מתודה שעברה בירושה מ-Animal
-- TODO: קראו ל-chirp() - מתודה של Bird עצמה
-- TODO: קראו ל-swim() רק אם הציפור יכולה לשחות
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
4פרויקט: בנק דיגיטלי
הגדרת הפרויקטפעולת הפקדה7פולימורפיזם ודריסת מתודות
דריסת מתודותקריאה למתודות של מחלקת האבטיפוסיות ברווזממשק משותףבדיקת טיפוססיכום – תפקידי עובדים2תבנית אב־טיפוס למחלקה
מושג אב־הטיפוסקישור באמצעות __indexהבנאי :new()אתחול מאפייניםמופעים עצמאייםסיכום – מפעל מכוניות5העמסת אופרטורים בתכנות מונחה עצמים
חיבור אובייקטיםחיסור אובייקטיםשרשור אובייקטיםהשוואת אובייקטים (<, >)חזרה – חישובים עם ארנק8כימוס
כללי מתן שמותClosures לשמירה על פרטיותגישה באמצעות Closuresטבלאות לקריאה בלבדלוגיקת אימותסיכום – כספת מאובטחת11תבניות עיצוב (בקצרה)
פונקציות Factoryטבלת Singletonתבנית IteratorObserver (מאזין)סיכום – מפעל Logger3מצב והתנהגות של אובייקטים
משתני מופעמתודות Getterמתודות Setterמאפיינים מחושביםעיצוב מחרוזותבדיקות שוויוןסיכום – ציון תלמיד6יסודות הירושה
הכנת מבנה הירושהירושת מתודותהרחבת הבנאיהוספת מתודות למחלקת הבתמשותף לעומת ייחודיסיכום – היררכיית צורות9קומפוזיציה ומיקסינים
קשר מסוג יש-לוהאצלהמיקסינים פשוטיםהחלת כמה מיקסיניםמיקסינים לעומת ירושהסיכום – הרכבת רובוטתרגלו בעצמכם: קומפיילר Lua אונליין