Builder Pattern – חלק ב (מעשי)

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

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

מימוש בעיית המכוניות בעזרת Builder Pattern

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

נתחיל בהגדרת האובייקט Car והמחלקות היורשות ממנו.

ניתן לראות לעיל שהגדרנו מחלקה כללית בשם Car ולה שתי מחלקות היורשות ממנה: RaceCar ו- PrivateCar.

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

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

בצד השני נראה כיצד מוגדרים הבנאים שלנו. קצת ארוך אבל לא באמת מורכב – 

נעבור על זה שלב אחר שלב.

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

לאחר מכן הגדרנו שני בנאים: PrivateCarBuilder ו- RaceCarBuilder. נשים לב לכמה דברים:

  • כל אחד מהבנאים מממש, מין הסתם, את כל הפונקציות שהוגדרו ב-ICarBuilder, אבל שימו לב שבמימוש בדוגמה זו הפונקציות כתובות בצורה דומה בשני הבנאים, אך אין זה מחייב, ובמקרים אחרים יתכן מאוד שכל פונקציה תבצע פעולות אחרות בכל בנאי.
  •  לכל בנאי הגדרנו גם פונקציית Build (פעמים שפונקציה זו נקראת Create או GetResult), בפונקציה זו אנו סוגרים את האובייקט ומחזירים למשתמש את אובייקט הסופי (RaceCar או  PrivateCar במקרה שלנו) ויחודי – על ידי שימוש בבנאי מעתיק.
  • בכל שאר הפונקציות אנחנו מחזירים ICarBuilder , במילים אחרות – this, וזה כדי שנוכל לבנות אובייקט בצורה משורשרת, כמו שנראה בהמשך.

כעת נעבור לקוד שעושה שימוש בכל הדבר הזה – 

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

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

ולסיום נראה את הפלט שיצא לנו מהרצה של התוכנית הזו – 

Race Car –
Color = Black
Model = 2019
Engine capacity =3000
Maximum speed = 250

Private Car –
Color = Red
Model = 2018
Engine capacity =1800
Maximum speed = 120

לסיכום

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

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

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

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *