meta class

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

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

עד כמה שיודע לי, למעט שפת פסקל ו objective c כל השפות האחרות שתומכות באפשרות הינן שפות דינמיות כדוגמת פיתון, רובי וכו' (את הרשימה המלאה אפשר למצוא בקישור שלי ל wikipedia).

אז כיצד עובדים עם Metaclass ?

ובכן דבר ראשון נגדיר מחלקה רגילה:

type
TMyNormalClass = class(TSomeParent)
...
procedure PushMessage(const Msg : String); virtual; abstrct;
...
end;

השלב הבא הוא להגדיר את ההגדרה הבאה:

TMyNormalClasses = class of TMyNormalClass;

מה שהגדרנו כאן זה התייחסות שכל דבר שהוא או TMyNormalClass או יורש ממנו יתקבל על ידי הטיפוס TMyNormalClasses.

כיצד נשתמש בזה אתם בטח שאולים את עצמכם, ובכן בצורה הבאה:

procedure PushMessage(const Msg : String; AInstance : TMyNormalClass; ANormalClass : TMyNormalClasses);
begin
if ANormalClass = TMyNormalClass then
raise Exception.Create('The given class is an abstract class.');
(AInstance as ANormalClass).PushMessage(Format('[%s] %s', [DateTimeToStr(now), Msg]);
end;

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

הסיבה ששמנו = ולא השתמשנו ב is היא בגלל שהמצב שלנו כאן הוא ש ANormalClass מכיל מחלקה ולא  Instance, ולכן אנחנו לא שואלים אם ה instance הוא מחלקה, אלא אנחנו שואלים האם מחלקה מסויימת היא המחלקה שאנחנו רוצים.

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

השורה של (AInstance as ANormalClass) היא השורה שעושה את הטריק. היא אומרת ל AInstance להתנהג בתור ANormalClass ולא בתור ה Instance שאנחנו מעבירים לה.

כיצד נשתמש בזה בפועל ?

type
TMyNormalImpelementation = class(TMyNormalClass)
...
procedure
TMyNormalImpelementation.PushMessage(const Msg : String); override;
...
end;
...
var
MyInstance : TMyNormalClass;
...
begin
...
MyInstance := TMyNormalImpelementation.Create .... ;
...
PushMessage('Hello world', MyInstance, TMyNormalImpelementation);
...
end.

ובעצם עכשיו כל מימוש של הרשימה שלנו (כולל בנים שירשו מ TMyNormalImpelementation יוכלו להיכנס בתור פרמטר ללא בעיה והמהדר שלנו לא ילין על כך בכלל.

חשוב להבין שגם אחרי שאתחלנו את MyInstance בתור TMyNormalImpelementation, אנחנו יכולים להעביר כל class שיורש מ TMyNormalClass.

15 מחשבות על “meta class

  1. ik_5 מאת

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

  2. מאיר

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

    להבדיל, אפשר לחשוב על metaclass בתור מחלקה אשר עוזרת ביצירת מחלקות אחרות.

  3. ik_5 מאת

    מאיר, אתה לא מפספס משהו, אלא הדגמתי עוד שימוש לזה. השימוש שאתה מדבר עליו הוא כמו בדוגמא בכתובת הבאה:
    http://freepascal.org/docs-html/ref/refse30.html
    תחפש class reference בתוך הדף הזה. שם הם מדגימים יצירה של מחלקה באמצעות המחלקה שהעברת בתור פרמטר.
    זוהי אחת מתוך כמה דוגמאות של דברים שאפשר ליצור בעזרת השיטה הזו.

  4. ik_5 מאת

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

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

    מקום שאתה יכול למצוא את זה בשימוש זה נגיד בספריית sockets (למשל lnet עובדת עם זה), כאשר אתה מקבל socket שיכול להיות כל סוג שיורש מ TLSocket, ואז אם יצרתי socket שבעצם מדבר ב HTTP, אני לא צריך לכתוב משהו מיוחד בשביל להשתמש באותן מתודות בשביל לעשות פעולות מסויימות רק בגלל שהמחלקה שונה.

    אני מקווה שזה יותר ברור.

  5. ארתיום

    את האמת — זה לא ממש ברור — ואני עוד זוכר משהו מ־pascal.

    אני באמת איבדתי אותך: לפי ההסבר מדובר בהחלפת virtual method במקום. (אם אני מבין נכון) קרי, יש לך vtable של אובייקט מסוים, אתה מחליף בו X ב־Y. האם זה נכון או לא? או שמא מדובר ב־dynamic casting?

  6. ik_5 מאת

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

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

  7. מאיר

    מה שמתואר בקישור של FPC אכן מדגים metaclass. לעומת זאת הקוד אשר מופיע כאן אמנם משתמש ב-class reference, אך רק עבור בדיקת ירושה ואינו מדגים metaclass.

  8. ik_5 מאת

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

  9. ik_5 מאת

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

  10. מאיר

    בפסקל תכונת ה-class reference מאפשרת metaclass. בפייתון, עקב האופי הדינאמי של השפה, משהו כמו class reference מיותר.

    בדוגמא בוויקיפדיה ה-AttributeInitType (בניצול ה-__call__) מחזיר לך מחלקה שהפרמטרים בעלי השם (kwargs) אשר מועברים בעת הקריאה אליה הופכים ל-attributes שלה. משמתשים בו בתור metaclass בחילול האובייקט Car אשר זוכה בתכונה הזו.

  11. פינגבק: meta class 2 « לראות שונה

  12. פינגבק: מערך כתכונה | לראות שונה

כתיבת תגובה

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

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s