מחלקות ותכנות מונחה־עצמים

bless#

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

bless משייך את הישות המוצבעת של REF לחבילה הנקובה בשם CLASSNAME, כך שקריאות מתודה דרך REF מבצעות דיספאץ« מול תת־השגרות של אותה חבילה (ואבותיה ב־@ISA). ההפניה עצמה אינה משתנה; מה שמשתנה הוא תג המחלקה של הישות המוצבעת. bless מחזיר את REF, ולכן הוא כמעט תמיד ההוראה האחרונה בבנאי.

תקציר#

bless REF, CLASSNAME
bless REF

מה מקבלים בחזרה#

אותה הפניה שהועברה כארגומנט הראשון, כעת נושאת את תג המחלקה של CLASSNAME. החזרת ההפניה מאפשרת לבצע tail call ל־bless מבנאי:

sub new {
    my ($class, %args) = @_;
    return bless { %args }, $class;
}

מאותה נקודה ואילך, ref על ההפניה המוחזרת מניב את שם המחלקה ולא את סוג ההפניה הגולמי (HASH, ARRAY, CODE, …).

צורות#

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

  • דו־ארגומנטית - תמיד להשתמש בזו.

    bless $ref, $class;
    

    הישות המוצבעת הופכת לעצם בחבילה הנקובה בשם $class. שימוש בארגומנט הראשון של הבנאי ($_[0], ככלל $class) שומר על הבנאי כניתן לירושה: תת־מחלקה שקוראת ל־Parent::new('Child', ...) מקבלת בחזרה עצם שעבר bless לתוך Child, לא לתוך Parent.

  • דו־ארגומנטית עם מחרוזת ריקה - bless לתוך main.

    bless $ref, "";
    

    CLASSNAME ריק מטופל כחבילת main. שימושי לעיתים נדירות מחוץ לתחבולות מכוונות.

  • חד־ארגומנטית - לא מומלצת.

    bless $ref;
    

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

דוגמאות#

מחלקה מינימלית מבוססת האש:

package Point;

sub new {
    my ($class, $x, $y) = @_;
    return bless { x => $x, y => $y }, $class;
}

sub x { $_[0]->{x} }
sub y { $_[0]->{y} }

package main;

my $p = Point->new(3, 4);
print ref $p, "\n";       # Point
print $p->x, ",", $p->y;  # 3,4

בנאי בר־ירושה - אותו new עובד עבור תת־מחלקה ללא שינוי:

package Shape;
sub new {
    my ($class, %args) = @_;
    return bless { %args }, $class;
}

package Circle;
our @ISA = ('Shape');
sub area {
    my $self = shift;
    return 3.14159 * $self->{radius} ** 2;
}

package main;
my $c = Circle->new(radius => 5);
print ref $c, "\n";       # Circle
print $c->area, "\n";     # 78.53975

עצמים מבוססי מערך עובדים באותו אופן - כל סוג הפניה ניתן ל־bless:

package Pair;
sub new {
    my ($class, $a, $b) = @_;
    return bless [$a, $b], $class;
}
sub first  { $_[0]->[0] }
sub second { $_[0]->[1] }

my $p = Pair->new("left", "right");
print $p->first, "/", $p->second;   # left/right

ביצוע bless מחדש לעצם קיים משנה את תג המחלקה שלו במקום:

my $obj = bless {}, 'OldClass';
bless $obj, 'NewClass';
print ref $obj;            # NewClass

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

Logger->new(file => $path)->info("started");
#                              ^ called on the blessed ref

מקרי קצה#

  • ההפניה היא מה שמתויג, לא המשתנה. עותקים של ההפניה חולקים את תג המחלקה משום שכולם מצביעים לאותה ישות מוצבעת:

    my $a = bless {}, 'Foo';
    my $b = $a;
    print ref $b;            # Foo
    
  • ביצוע bless מחדש מותר ומיידי. ניתן לבצע bless להפניה כמה פעמים שרוצים; כל קריאה דורסת את תג המחלקה הקודם. אין built-in של ״unbless״ - יש לבצע bless מחדש לחבילה ניטרלית אם צריך להסיר עצמיות מישות מוצבעת שעדיין מחזיקים אליה הפניות.

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

  • יש להימנע מ־bless $ref, "0". שם המחלקה "0" הוא ערך שקרי, ולכן קוד שבודק if (ref $obj) { ... } יפרש את העצם בטעות כ״לא הפניה״. יש לבחור כל שם מחלקה שאינו שקרי.

  • מוסכמת רישיות שם־מחלקה. יש להשתמש ברישיות מעורבת (My::Package). שמות ALL-CAPS שמורים לטיפוסי built-in של Perl (SCALAR, ARRAY, HASH, CODE, …) ושמות ברישיות־קטנה שמורים לפראגמות (strict, warnings, utf8). ביצוע bless לכל אחד ממרחבי שמות אלה מזמין בלבול עם מנגנוני רמת השפה.

  • ארגומנט ראשון שאינו הפניה הוא שגיאה קטלנית. bless 42, 'Foo' מת עם Can't bless non-reference value. הארגומנט הראשון חייב להיות הפניה.

  • bless חד־ארגומנטי בוחר את חבילת זמן־ההידור. הוא משתמש בחבילה שהקריאה ל־bless נמצאת בתוכה לקסיקלית - לא חבילת הקורא, לא חבילתו של $class. בדיוק בגלל זה הוא שובר ירושה ובגלל זה הצורה הדו־ארגומנטית קיימת.

  • הפניות חלשות ו־blessing מתקשרות כצפוי. Scalar::Util::weaken על הפניה blessed לא מבטלת אותה; החריץ המוחלש עדיין רואה את תג המחלקה, ו־ref עובד עד שהישות המוצבעת מושמדת.

  • DESTROY נורה על הישות המוצבעת ה־blessed. כאשר ההפניה האחרונה לישות מוצבעת blessed נעלמת, Perl קורא ל־DESTROY במחלקה של ההפניה (בהליכה אחר @ISA). שינוי המחלקה ב־bless מאוחר משנה איזה DESTROY רץ.

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • ref - קריאת תג המחלקה בחזרה מהפניה blessed, או סוג ההפניה (HASH, ARRAY, …) עבור הפניה לא־blessed

  • package - הצהרה על החבילה שאליה מהדרת קריאה ל־bless או הגדרת תת־שגרה; היעד של bless חד־ארגומנטי

  • class - ה־built-in הניסיוני לתחביר מחלקה שמחליף את תבנית הבנאי הידנית package + bless בתחביר דקלרטיבי

  • tie - צורה שונה של קסם לכל־משתנה; לא קשורה ל־bless למרות המסגור השטחי של ״חיבור התנהגות לערך״

  • @ISA - שרשרת הירושה המתייעצים בה בעת ביצוע דיספאץ« לקריאת מתודה על הפניה blessed

  • DESTROY - מתודת ההורס ש־Perl קורא לה על הישות המוצבעת ה־blessed כאשר ההפניה האחרונה אליה נעלמת; מוצהרת כתת־שגרה בתוך חבילת המחלקה