מיקסינים לעומת ירושה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Lua של Coddy. שיעור 53 מתוך 70.
למדת שתי דרכים לשתף התנהגות: ירושה יוצרת קשר של "הוא-סוג-של" דרך שרשרת האב־טיפוס, ואילו מיקסינים מעתיקים פונקציות ישירות למחלקה. אבל מתי כדאי להשתמש בכל אחת מהגישות?
השתמשו בירושה כאשר לאובייקטים יש זהות בסיסית משותפת. Bird הוא Animal. הגיוני ש-Bird יירש התנהגויות בסיסיות של בעלי חיים, כמו :eat() או :sleep(). הקשר הזה קבוע ומגדיר מהו האובייקט במהותו.
השתמשו במיקסינים כשאתם רוצים להוסיף יכולת שאינה מגדירה את זהות הליבה של האובייקט.
לא כל הציפורים יכולות לעוף: פינגווינים ויענים הם ציפורים, אבל הם לא עפים. תעופה היא יכולת, ולא תכונה שמגדירה ציפור. mixin בשם Flyable מאפשר להוסיף יכולת תעופה לציפורים שזקוקות לה, בלי לכפות אותה על כולן.
שתי הגישות פועלות יחד. כאן גם Eagle וגם Penguin יורשות מ־Bird, ורק Eagle מקבלת את המיקסין:
-- Bird יורשת מ-Animal (יחס של "הוא סוג של")
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- גם Eagle וגם Penguin הן ציפורים
local Eagle = setmetatable({}, {__index = Bird})
Eagle.__index = Eagle
local Penguin = setmetatable({}, {__index = Bird})
Penguin.__index = Penguin
-- Flyable היא יכולת (מיקסין)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- יש להחיל את המיקסין רק על הציפורים שעפות
applyMixin(Eagle, Flyable)נשר יכול לקרוא ל־:fly(). פינגווין לא יכול: fly לא נמצא ב־Penguin, ב־Bird או ב־Animal, ולכן penguin:fly() הוא שגיאה. החלת המיקסין על Bird במקום זאת תעתיק את fly לכל ציפור, כולל פינגווינים.
אתגר
קלבואו נבנה מערכת אקולוגית קטנה של יצורים שמדגימה מתי להשתמש בירושה לעומת במיקסינים! תיצרו מחלקת בסיס 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)צריך לקרוא לבנאי של מחלקת האב. הוסיפו מתודת:chirp()שמדפיסה{name} chirps!. זוהי התנהגות ייחודית לציפורים. אל תוסיפו שחייה למחלקת Bird: לא כל ציפור יכולה לשחות.main.lua: כתבו כאן את פונקציית העזרapplyMixin(target, mixin): היא מעתיקה כל פונקציה מהמיקסין אל היעד. קראו את שם הציפור והאם היא יכולה לשחות (yesאוno). צרו מופע של Bird, ואם היא יכולה לשחות, החילו את המיקסין Swimmable על הציפור הזאת בלבד. לאחר מכן קראו ל-:eat()(שירושה מ-Animal) ול-:chirp()(המתודה של Bird עצמה), וקראו ל-:swim()רק אם המתודה קיימת אצל הציפור הזאת (if bird.swim then).
תקבלו שתי קלטים:
- שם הציפור (מחרוזת)
- האם הציפור יכולה לשחות:
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 (ירושה), אבל שחייה היא יכולת אופציונלית (מיקסין). מכיוון שהמיקסין מתווסף רק לציפורים שיכולות לשחות, לציפור שלא יכולה לשחות אין כלל מתודת swim.
נסו בעצמכם
-- main.lua: נקודת כניסה - יצירת ציפור והדגמת היכולות שלה
-- פונקציית עזר להחלת mixin על מחלקה או על אובייקט יחיד
local function applyMixin(target, mixin)
-- TODO: להעתיק כל פונקציה מ-mixin אל target
end
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- קריאת הקלטים
local name = io.read()
local canSwimInput = io.read()
-- TODO: ליצור מופע של Bird עם השם
-- TODO: אם הקלט הוא "yes", להחיל את ה-mixin של Swimmable על הציפור הזו בלבד
-- 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 אונליין