מימוש תבנית Delegation ב Python

08/05/2017

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

כשחושבים על שיתוף קוד בין מחלקות מתכנתים תמיד נזכרים בירושה. אבל דווקא תבנית ה Delegation נותנת הרבה פעמים פתרון נקי יותר. בדוגמא שלנו נבנה את התבנית ב Python במספר גישות שונות כדי לראות את ההבדלים ביניהן.

1. מימוש התבנית בקוד המחלקה

בשביל הדוגמא נגדיר מחלקה בשם Contact שתשמור פרטי איש קשר. את המחלקה נרחיב באמצעות מחלקות עבור דרכי התקשורת השונות, למשל Email, Phone ו SnailMail. כל דרך תקשורת תוסיף פונקציות לשימוש בה - לדוגמא מחלקת Phone תאפשר לנו להפעיל sendsms כדי לשלוח sms. הנה הקוד בגירסתו הראשונה עבור איש קשר עם אפשרות לשליחת sms:

class Phone:
    def __init__(self, number):
        self.number = number

    def sendsms(self, msg):
        print("Sending to: {}. Message: {}".format(self.number, msg))

class Contact:
    def __init__(self, number):
        self.phone = Phone(number)

    def sendsms(self, msg):
        self.phone.sendsms(msg)

c = Contact('052-1121312')
c.sendsms('hello')

כך הרווחנו מצד אחד הפרדה בין המחלקות (כל מחלקה מטפלת בתחום אחריותה בלבד) ומצד שני היישום נקי מקשר של ירושה בין המחלקות.

הבעיה שעכשיו בכל פעם שנוסיף פונקציה ל Phone נצטרך לעדכן ידנית את Contact ולהוסיף פונקציה שרק תקרא לפונקציה המתאימה לה מ Phone, משהו שבירושה היינו מקבלים במתנה.

2. הגדרת פונקציה בבנאי

אפשר לקצר את הקוד אם ניזכר שפונקציה היא בסך הכל attribute ואפשר להגדירה גם דינמית מתוך קוד הבנאי:

class Phone:
    def __init__(self, number):
        self.number = number

    def sendsms(self, msg):
        print("Sending to: {}. Message: {}".format(self.number, msg))

class Contact:
    def __init__(self, number):
        self.phone = Phone(number)
        setattr(self, 'sendsms', self.phone.sendsms)

c = Contact('052-1121312')
c.sendsms('hello')

קיבלנו את אותה התוצאה רק שעכשיו הגדרת הפונקציות הרבה יותר קצרה. אפשר להמשיך ולשפר ולהפוך את הקוד לגנרי, למשל באמצעות הגדרת הפונקציה delegates הבאה:

def delegates(frm, to, *methods):
    for name in methods:
        setattr(frm, name, getattr(to, name))

class Phone:
    def __init__(self, number):
        self.number = number

    def sendsms(self, msg):
        print("Sending to: {}. Message: {}".format(self.number, msg))

    def call(self):
        print("Calling: {}".format(self.number))

class Contact:
    def __init__(self, number):
        self.phone = Phone(number)
        delegates(self, self.phone, 'sendsms', 'call')



c = Contact('052-1121312')
c.sendsms('hello')
c.call()

הפונקציה delegates מאפשרת הגדרת חיבור מסוג Delegation בין שתי מחלקות, כך שקוד המשתמש במחלקות לא מעורב בחלוקת העבודה הפנימית בתוכן.