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

tie#

לקשור משתנה למחלקה כך שכל גישה למשתנה תופנה דרך המתודות של אותה מחלקה.

tie מקסים את VARIABLE כך שקריאות, כתיבות, ופעולות אחרות עליו מועברות למתודות ממשק ה־tie של CLASSNAME (FETCH, STORE, FIRSTKEY, PRINT, …). LIST מועבר לקונסטרקטור של המחלקה (TIESCALAR, TIEARRAY, TIEHASH, או TIEHANDLE, תלוי ב־sigil של VARIABLE). הקונסטרקטור מחזיר את העצם הקשור הבסיסי, ועצם זה - לא VARIABLE - הוא ערך ההחזרה של tie. יש לשמור אותו אם רוצים לקרוא למתודות נוספות על המימוש ישירות.

חוזה המתודות שכל מחלקה חייבת לקיים מתועד באריכות ב־perltie; עמוד זה מתעד את tie המובנה.

תקציר#

tie my $scalar, 'ScalarClass', @args;
tie my @array,  'ArrayClass',  @args;
tie my %hash,   'HashClass',   @args;
tie *FH,        'HandleClass', @args;

my $obj = tie my %h, 'HashClass', @args;   # keep the tied object

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

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

my $db = tie my %cache, 'My::Cache', path => '/var/cache/app.db';
$db->flush;                 # method on the tied object
$cache{key} = 'value';      # goes through STORE

tie אינו מחזיר את המשתנה עצמו. קוד שכותב my $x = tie ... ואז מתייחס ל־$x כסקלר הוא טעות מתחילים נפוצה; הסקלר הוא $x רק אם גם קשרת את $x, ויעד ההשמה הוא העצם הקשור, לא המשתנה.

לשחזור העצם הקשור ממשתנה מאוחר יותר, יש להשתמש ב־tied. לפירוק הקישור, יש להשתמש ב־untie.

איזה קונסטרקטור נקרא#

ה־sigil של VARIABLE בוחר את הקונסטרקטור. LIST מועבר verbatim:

צורת VARIABLE

קונסטרקטור שנקרא

הפניית ממשק

$scalar

CLASSNAME->TIESCALAR(LIST)

Tie::Scalar

@array

CLASSNAME->TIEARRAY(LIST)

Tie::Array

%hash

CLASSNAME->TIEHASH(LIST)

Tie::Hash

*FH (glob)

CLASSNAME->TIEHANDLE(LIST)

Tie::Handle

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

מתודות שכל מחלקה חייבת לספק#

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

סקלר קשור (TIESCALAR):

TIESCALAR classname, LIST
FETCH     this
STORE     this, value
DESTROY   this
UNTIE     this

מערך קשור (TIEARRAY):

TIEARRAY  classname, LIST
FETCH     this, index
STORE     this, index, value
FETCHSIZE this
STORESIZE this, count
CLEAR     this
PUSH      this, LIST
POP       this
SHIFT     this
UNSHIFT   this, LIST
SPLICE    this, offset, length, LIST
EXTEND    this, count
DELETE    this, index
EXISTS    this, index
DESTROY   this
UNTIE     this

האש קשור (TIEHASH):

TIEHASH   classname, LIST
FETCH     this, key
STORE     this, key, value
DELETE    this, key
CLEAR     this
EXISTS    this, key
FIRSTKEY  this
NEXTKEY   this, lastkey
SCALAR    this
DESTROY   this
UNTIE     this

מטפל קובץ קשור (TIEHANDLE):

TIEHANDLE this, LIST
READ      this, scalar, length, offset
READLINE  this
GETC      this
WRITE     this, scalar, length, offset
PRINT     this, LIST
PRINTF    this, format, LIST
BINMODE   this
EOF       this
FILENO    this
SEEK      this, position, whence
TELL      this
OPEN      this, mode, LIST
CLOSE     this
DESTROY   this
UNTIE     this

ראו perltie למפרט המלא (חתימות מתודות, מתי כל אחת נקראת, ברירות מחדל שמורשות ממחלקות הבסיס Tie::*).

דוגמאות#

לקשור האש לקובץ DBM על הדיסק ולעבור עליו עם each. keys ו־values היו מממשים את כל הקבוצה בבת אחת - עבור קבצי DBM גדולים זה יקר:

use NDBM_File;
tie(my %HIST, 'NDBM_File', '/usr/lib/news/history', 1, 0);
while (my ($key, $val) = each %HIST) {
    print $key, ' = ', unpack('L', $val), "\n";
}

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

my $impl = tie my %cache, 'My::Cache', ttl => 60;
$cache{session} = $data;    # STORE
$impl->expire_now;           # direct method call

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

package Tie::Logged {
    sub TIESCALAR { my ($class, $v) = @_; bless \$v, $class }
    sub FETCH     { my $self = shift; warn "read\n";  $$self }
    sub STORE     { my ($s, $v) = @_;  warn "write\n"; $$s = $v }
}

tie my $x, 'Tie::Logged', 42;
print $x;           # warns "read",  prints 42
$x = 99;            # warns "write"

לקשור מטפל קובץ למחלקה שלוכדת פלט לתוך מחרוזת:

package Tie::StringHandle {
    sub TIEHANDLE { my $c = shift; bless { buf => '' }, $c }
    sub PRINT     { my $s = shift; $s->{buf} .= join '', @_; 1 }
    sub buf       { $_[0]->{buf} }
}

tie *OUT, 'Tie::StringHandle';
print OUT "hello\n";
print OUT "world\n";
my $obj = tied *OUT;
print $obj->buf;    # "hello\nworld\n"

מקרי קצה#

  • ערך ההחזרה הוא העצם, לא המשתנה. my $r = tie my %h, ... קושר את $r לעצם הקשור. השמה ל־$r משימה לסקלר; היא אינה כותבת דרכו ל־%h.

  • המודול חייב להיטען קודם. שלא כמו dbmopen, tie אינו מבצע use או require ל־CLASSNAME עבורך. יש לטעון אותו במפורש:

    use My::TiedClass;
    tie my %h, 'My::TiedClass';
    

    שכחת זה היא הסיבה הטיפוסית של Can't locate object method "TIEHASH" via package ....

  • מתודות חסרות הן קטלניות בנקודת השימוש, לא בזמן ה־tie. האש קשור שהמחלקה שלו מגדירה FETCH אבל לא STORE נקשר בהצלחה, ואז מעלה Can't locate object method "STORE" ... בהשמה הראשונה.

  • אי־התאמת sigil היא שגיאת זמן tie. tie בוחר את הקונסטרקטור מה־sigil של המשתנה, כך שמחלקה שמגדירה רק TIESCALAR אינה יכולה להיקשר ב־tie להאש - החיפוש של TIEHASH נכשל בקריאת ה־tie עצמה.

  • tie על משתנה קשור מבצע untie משתמע לקישור הקודם קודם, מפעיל UNTIE ובסופו של דבר DESTROY על העצם הישן (ברגע שההפניה האחרונה אליו נשמטת).

  • local על משתנה קשור שומר ומשחזר את הקישור, לא רק את הערך. ביציאת התחום המשתנה נקשר מחדש לעצם המקורי שלו.

  • החזקת הפניה נוספת לעצם הקשור משאירה אותו בחיים מעבר ל־untie. תחת use warnings זה מייצר אזהרת untie attempted while N inner references still exist. יש להשליך את ההפניה (לדוגמה undef $obj) לפני קריאה ל־untie אם רוצים ש־DESTROY יופעל מיד.

  • tie אינו מאחסן את העצם בתוך המשתנה בשום דרך גלויה למשתמש - יש להשתמש ב־tied כדי לאחזר אותו; אין תחביר dereference שחושף אותו.

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

הבדלים מ־upstream#

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

ראו גם#

  • tied - לשחזר את העצם שמשתנה קשור אליו כעת, או undef אם אינו קשור

  • untie - לפרק את הקישור ולתת לעצם הקשור להיהרס

  • bless - הפרימיטיב ברמה נמוכה שקונסטרקטורי TIE* משתמשים בו כדי להפוך הפניה לעצם

  • dbmopen - קיצור ישן יותר למקרה הספציפי של קשירת האש לקובץ DBM; טוען את מחלקת ה־DBM עבורך, מה ש־tie אינו עושה

  • ref - לבחון את המחלקה של העצם המוחזר על־ידי tie