You are on page 1of 30

‫‪Design Patterns 4‬‬

‫‪Design Patterns‬‬
‫מאיר סלע‬

‫מהדורה ראשונה ‪2004‬‬


‫הדפסה ‪1‬‬
‫כל הזכויות שמורות‬

‫מרכז ההדרכה עיט ‪2000‬‬


‫אתר אינטרנט‪www.mh2000.co.il :‬‬
‫דואר אלקטרוני‪info@mh2000.co.il :‬‬

‫אי להעתיק‪ ,‬לשכפל או לצל ספר זה או קטעי ממנו‪ ,‬בשו צורה ובשו אמצעי אלקטרוני‪,‬‬
‫אופטי או מכני לכל מטרה שהיא‪ ,‬ללא אישור בכתב מההוצאה לאור‪.‬‬
‫‪13‬‬ ‫פרק ‪ :1‬מבוא‬

‫‪ .1‬מבוא‬
‫נושאי הפרק‪:‬‬
‫• ‪  Design Patterns‬הגדרה‪ ,‬מאפייני
‪ ,‬יישו

‫• מדדי
בפיתוח תוכנה  וכיצד ‪ Design Patterns‬מסייעי
בהשגת

‫• ‪  Reference‬מקורות ספרות‬
‫‪Design Patterns 14‬‬

‫‪Design Patterns‬‬
‫עצמי‬ ‫מונחה‬ ‫‪ design patterns‬הוא תחו במסגרת הכללית של תיכו

‫)‪ ,(Object Oriented Design‬הכולל טכניקות תיכו ותכנות מתקדמות‪.‬‬
‫המקור התיעודי העיקרי ל ‪ patterns‬הוא הספר]‪ .[Gamma95‬לספר ארבעה מחברי ‪ ,‬ולכ ה ‬
‫מכוני לפעמי בקיצור ג ‪ .Gang Of Four  GOF‬מחברי הספר עצמ מעידי שמקור‬
‫ההשראה העיקרי עבור היה ספרו של כריסטופר אלכסנדר ]‪ [Alexander79‬שעסק בתכנו‬
‫וארכיטקטורה אורבניי ‪.‬‬
‫בספר זה נסקור את ה ‪ patterns‬התקניי והמוכרי בתעשיית התוכנה‪ .‬לכל ‪ pattern‬נית ש ‬
‫מזהה ותיאור בשלושה חלקי ‪:‬‬
‫‪ −‬תיאור הקשר )‪ (context‬הבעיה תו התבוננות בדוגמא‬
‫‪ −‬תיאור הפתרו בצורת תרשי תיכו ‪ ,UML‬כמו ג קוד דוגמא ב ‪.C++/Java/C#‬‬
‫‪ −‬הכללת הפתרו למקרה הכללי תו ציו קשרי ע ‪ patterns‬אחרי ‪ ,‬וריאציות שונות‬
‫של הפתרו‪ ,‬השלכות‪ ,‬ושימושי לדוגמא‪.‬‬

‫כפי שנאמר‪ ,‬המקור העיקרי ל ‪ patterns‬הוא הספר ]‪ .[Gamma95‬עקב גילו‪ ,‬שפת התרשימי ‬
‫שבו אינה עדכנית )לא ‪ ,(UML‬מערכות העצמי ודוגמאות הקוד שבו מיושנות‪ .‬ספר זה עושה‬
‫שימוש בתרשימי ‪ ,UML‬כולל עדכוני רבי ‪ ,‬תוספות ודוגמאות ממערכות עצמי מודרניות‪.‬‬

‫‪  Design Pattern‬מה זה?‬


‫קיימות מספר הגדרות ל ‪ ,design pattern‬אול הדר הבהירה ביותר להבנתו היא באמצעות‬
‫מאפייניו‪ pattern .‬מצוי ‪:‬‬
‫• מספק פתרו לבעיה כללית החוזרת ומופיעה בצורות שונות‬
‫• יכול לכלול מספר וריאציות של הפתרו )בהתא לוריאציות של הבעיה ולשיקולי תיכנו‬
‫נוספי (‬
‫• נבדק ונוסה כבר פעמי רבות )בהצלחה(‬
‫• בד"כ מתייחס לתחושת ‪ dejavu‬של מפתחי מנוסי בהקשר הנתו‬
‫• לרוב ממומש ע"י שיתו פעולה בי מספר מחלקות‪/‬עצמי ‬
‫• לרוב הוא אינו המצאה או גילוי של אד מסויי ‬
‫‪15‬‬ ‫פרק ‪ :1‬מבוא‬

‫הגדרה של מחברי הספר ‪:GOF‬‬


‫‪Design Patterns - “Descriptions of communicating objects and classes that are‬‬
‫”‪customized to solve a general design problem in a particular context.‬‬

‫הגדרה של אלכסנדר בספרו ]‪: [Alexander79‬‬


‫‪“Each pattern is a three-part rule, which expresses a relation between a certain‬‬
‫“‪context, a problem, and a solution.‬‬

‫מטרות בלימוד ה ‪patterns‬‬


‫המטרות העיקריות בלימוד ה ‪ patterns‬שבספר זה ה‪:‬‬
‫• הכרה וזיהוי של תבניות בעיות ופתרונ  בכדי לזהות ‪ pattern‬ראשית יש להכירו‪ ,‬ולהפני‬
‫את המבנה שלו‪ ,‬כמו ג את שמו‪ .‬לא תמיד קל לזהות ‪ ,patterns‬אול הזמ והתרגול‬
‫מסייעי בכ‪.‬‬
‫• רכישת שפה "גבוהה" לביטוי ולתיעוד התבניות הנ"ל  לאחר תהלי ההפנמה של ה ‪patterns‬‬
‫נית לקטו פירות נוספי‪ :‬קיבלנו שפת ‪ patterns‬שנית להשתמש בה במסגרת תיכו‪ ,‬ייעו‪,‬‬
‫שיחה‪ ,‬תיעוד ולימוד‪.‬‬
‫לדוגמא‪ ,‬במקו לכתוב בתיעוד מחלקה מסויימת בתכנית שהגדרת את ה ‪ constructor‬כ‬
‫‪ protected‬בכדי למנוע ייצור מפורש של עצמי מהמחלקה‪ ,‬א סיפקת פונקציה סטטית‬
‫לקבלת המופע היחיד שלו‪ ,‬אתה יכול להשתמש במילה אחת השקולה לתיאור הנ"ל‪:‬‬
‫‪.Singleton‬‬
‫• לימוד מניסיונ של אחרי בנושאי תיכו ותכנות  ה ‪ patterns‬ה למעשה מאגר של הידוע‬
‫עד כה בנושאי תכנות מונחה עצמי‪ .‬מאגר זה נאס ונבדק במש שני על סמ נסיונ של‬
‫רבי וטובי‪ ,‬והצגת הידע הזה כקטלוג של טכניקות תכנות מקצרת את תהלי הלימוד של‬
‫ה"תורה שבעלפה" עבור מהנדסי תוכנה פחות מנוסי ב ‪.OO‬‬

‫תוצרי לוואי‪:‬‬
‫• לימוד ותרגול ‪ UML‬כשפה לתיאור מודלי תוכנה  מרבית הדיוני שבספר זה מתבצעי‬
‫ברמת התיכו ב ‪ ,UML‬כאשר פה וש מובאות דוגמאות קוד‪ .‬אחת התוצאות של לימוד ה‬
‫‪ patterns‬היא הכרת ‪ UML‬בצורה מחודדת ומעמיקה‪.‬‬
‫• התנסות בתיכו ובמעבר מתיכו למימוש  שיקולי התיכו שמלווי חלק גדול מה ‪patterns‬‬
‫מספקי התנסות עשירה בבחינת השפעת במעבר למימוש‪.‬‬
‫‪Design Patterns 16‬‬

‫• הכרה והתנסות ע מנגנוני מתקדמי ב ‪ :OOP‬פולימורפיז ופולימורפיז טהור‪,‬‬


‫‪.RTTI ,templates / Generics‬‬
‫• חשיפה למערכות עצמי מתקדמות בספר מובאות דוגמאות בשפות ‪C# ,Java ,C++‬‬
‫ובמערכות ‪ .MFC, .NET, Java, COM‬ה ‪ patterns‬עוזרי להבי בקלות ובמהירות מערכות‬
‫וספריות מורכבות מאוד‪.‬‬

‫מדדי
בפיתוח תוכנה‬
‫המטרה המשותפת לכל ה ‪ patterns‬שבספר זה היא פיתוח תוכנה "טובה" יותר‪ .‬השאלה‬
‫הראשונה שעולה מכא היא "מהי תוכנה טובה יותר?" לש כ קיימי מספר מדדי מקובלי‬
‫להערכת תוכנה‪:‬‬
‫• גמישות היכולת לבצע שינויי בתוכנה ללא מאמ רב‪.‬‬
‫• הרחבה היכולת להרחיב את התוכנה )בדר כלל פרוייקטי תוכנה מתרחבי ע הזמ(‪.‬‬
‫• יעילות ניצול משאבי מקו וזמ שצורכי מרכיבי המערכת‪.‬‬
‫• בהירות היכולת ללמוד את המערכת ללא מאמ רב ובזמ קצר‪.‬‬
‫• ניידות יכולת הסבה של התוכנה לספריות‪ ,‬מערכות ומחשבי שוני‪.‬‬
‫במילי אחרות‪ ,‬תוכנה היא טובה יותר ככל שהיא יעילה יותר‪ ,‬גמישה יותר בפני ביצוע שינויי‪,‬‬
‫מאפשרת תוספות בצורה קלה ופשוטה יותר וככל שנית להסב אותה לסביבות שונות ביתר‬
‫קלות‪.‬‬
‫מדד אחר שמקובל להגדיר באופ כללי הוא פשטות‪ ,‬והוא כולל בתוכו את כל המדדי האחרי‪:‬‬
‫ככל שהמערכת פשוטה וטבעית יותר‪ ,‬היא מובנת יותר‪ ,‬נוחה יותר להרחבה ולשינוי ולכ טובה‬
‫יותר‪ .‬באנגלית קיי קיצור ‪ KIS‬לביטוי "‪."Keep It Simple‬‬

‫מדדי פרטניי יותר‬


‫השאלה‪ :‬כיצד בוני תוכנה שתענה לדרישות הנ"ל יעילה‪ ,‬בעלת יכולת גמישות‪ ,‬יכולת הרחבה‬
‫ויכולת נייוד טובות יותר?בכדי לענות לשאלה‪ ,‬עלינו לפרוט את המדדי הנ"ל למדדי משנה‪:‬‬
‫• מודולריות מערכת מודולרית היא מערכת המחולקת למרכיבי תוכנה‪ ,‬באופ היררכי‪ ,‬כ‬
‫שכל נושא בה מטופל ע"י מרכיב אחד‪ .‬מדד זה נקבע בשלבי התיכו המוקדמי של המערכת‪:‬‬
‫בתהלי ה ‪ decomposition‬מכריעי כיצד לפרק את המורכבות של המערכת לכדי מודולי‪,‬‬
‫כ שהצימוד )להל( ביניה יהיה קט ככל האפשר‪ .‬כמו כ‪ ,‬תכנו מודולרי של המערכת‬
‫מאפשר שימוש חוזר )‪ (Reusability‬במרכיביה ביתר קלות‪.‬‬
‫‪17‬‬ ‫פרק ‪ :1‬מבוא‬

‫• צימוד )‪  Decoupling / (Coupling‬אלו ה שני הפכי‪ :‬בתכנו מערכת אנו מנסי להשיג‬
‫‪ decoupling‬מקסימלי בי רכיבי תוכנה שוני‪ .‬בדר כלל‪ ,‬ככל שהצימוד קט יותר קל יותר‬
‫לבצע שינויי והרחבות‪ .‬פולימורפיז הוא מנגנו יעיל מאוד לפיתוח רכיבי תוכנה ע צימוד‬
‫מינימלי‪ .‬כמעט כל ה ‪ patterns‬מקטיני את הצימוד שבי מרכיבי שוני ה ע"י שמוש‬
‫בפולימורפיז וה ע"י שימוש בהפנייה )‪.(Forwarding‬‬
‫לדוגמא‪ ,‬ה ‪) Command‬עמוד ‪ (185‬מקטי את הצימוד שבי יוז הפקודה לבי העצ‬
‫המקבל ומבצע אותה‪ .‬יתר על כ‪ ,‬היוז עצמו אינו מכיר ולכ ג לא תלוי בסוגי ה‬
‫‪ Commands‬הקיימי במערכת‪ ,‬בזכות המבנה הפולימורפי שלה‪ .‬מה שאומר ששינוי כלשהו‬
‫במבצעי הפקודות‪ ,‬או הוספת ‪ Commands‬חדשי למערכת‪ ,‬אינ מצריכי הידור מחודש‬
‫שלו‪.‬‬
‫ה ‪) Proxy‬עמוד ‪ (105‬מקטי את הצימוד שבי קוד הלקוח לבי עצ נתו ע"י חציצה בי‬
‫השניי וביצוע הפנייה )‪ (Forwarding‬של קריאות הלקוח לעצ המטרה‪.‬‬
‫• ניצול מקו
טוב יותר  בתכנו נכו יותר של המערכת נית לצרו פחות מקו בזכרו‬
‫התכנית‪ ,‬ה זה שבשימוש ע"י הקוד וה זה שבשימוש ע"י הנתוני‪ .‬ה ‪) Proxy‬עמוד ‪ (105‬וה‬
‫‪) Flyweight‬עמוד ‪ (162‬מספקי ניצול טוב יותר של זכרו התכנית ע"י שימוש בשיתו‪.‬‬
‫• ניצול זמ טוב יותר  הדר שבה מבוצעות פעולות במערכת יכולה להשפיע בסדרי גודל על‬
‫ביצועי הזמ שלה‪ .‬לדוגמא‪ ,‬שימוש בפולימורפיז בתכנו ‪ OO‬הוא יעיל יותר משימוש‬
‫במשפטי תנאי כגו‪  switch-case :‬קריאה לפונקציה וירטואלית מתבצעת ב )‪ ,O(1‬בעוד ש‬
‫‪ switch-case‬בוחר את הכניסה המתאימה ב )‪ ,O(n‬כאשר ‪ n‬הוא מספר ה ‪case‬י‬
‫שבמשפט‪.‬‬
‫מגוו של ‪ patterns‬התנהגותיי מייעלי את זמ הביצוע של משימות באופ הנ"ל‪:‬‬
‫‪) Command‬עמוד ‪) State ,(185‬עמוד ‪) Visitor ,(199‬עמוד ‪) Prototype ,(232‬עמוד ‪,(60‬‬
‫‪) Prototype-based Factory‬עמוד ‪(64‬‬
‫פרמטר זמ נוס הוא מש הפיתוח של המערכת‪ :‬ג כא‪ ,‬נית בבירור לומר שתוכנה‬
‫מודולרית ופשוטה יותר תהיה בעלת מש פיתוח קצר יותר מזה של מערכת פחות מודולרית‪.‬‬
‫• תלות )‪ / (Dependency‬איתלות  עפ"י דרישות היישו‪ ,‬אנו משתדלי להבי אילו תלויות‬
‫עלולות להכביד על ביצוע שינויי והרחבות במערכת כבר בשלב התכנו‪ .‬בתכנו שלוקח‬
‫בחשבו שינויי עתידיי )”‪ (“Design for Change‬מקטיני למינימו את התלויות‬
‫במרכיבי העשויי להשתנות‪.‬‬
‫לדוגמא‪ ,‬א אלגורית המוגדר ע"י פונקציה במחלקת תוכנה כלשהי עשוי להשתנות‪ ,‬וא‬
‫מעבר לכ האפשרות להחליפו באלגוריתמי אחרי באופ דינמי עשויה לקד את הפיתוח‪,‬‬
‫רצוי לשקול שימוש ב ‪) Strategy‬עמוד ‪.(245‬‬
‫• יכולות דינמיות  פה אנו מדברי על האפשרות לבצע שינויי ותוספות בזמ ריצה‪ .‬לדוגמא‪,‬‬
‫תכנו מסויי של יישו הכולל תפריט עשוי לטפל בכל האפשרויות הקיימות בתפריט בזמ‬
‫קומפילציה‪ .‬אול‪ ,‬הא נית להוסי פריטי או להסיר פריטי מהתפריט בזמ ריצה?‬
‫יכולת דינמית כזו חוסכת את הצור בקידוד ובהידור מחודש בכל שינוי בתפריט‪ .‬ה‬
‫‪Design Patterns 18‬‬

‫‪) Command‬עמוד ‪ (185‬מתאר דר יעילה למענה לדרישה זו‪.‬‬


‫באופ דומה‪) State ,‬עמוד ‪ (199‬מספק יכולת דינמית בקביעת מצב העצ‪ ,‬כמו ג להוסי‬
‫ולבטל מצבי בזמ ריצה‪) Strategy .‬עמוד ‪ (245‬מספק יכולת דינמית בקביעת סוג‬
‫האלגורית שיופעל בקריאה לפונקציה מסויימת של עצ נתו‪.‬‬

‫מיפוי דרישות ספציפיות ל ‪patterns‬‬


‫‪ design patterns‬מיועדי למת מענה לדרישות מסויימות‪ ,‬בהקשר נתו‪ .‬בסעי זה נראה מיפוי‬
‫של דרישות ספציפיות ל ‪ patterns‬המתאימי‪:‬‬
‫• איתלות באופ הייצוג והמימוש של העצ  לפי דרישה זו קוד הלקוח לא צרי להיות מודע‬
‫לפרטי הקוד של המחלקה‪.‬‬
‫)‪Adapter (156), Bridge (166), Facade (133), Proxy (105), Memento (224‬‬
‫• איתלות באופ יצירת העצ  קוד הלקוח לא צרי להיות מודע לאופ יצירת העצמי‪.‬‬
‫במקרי רבי‪ ,‬דרישה זו היא תוצאה ישירה של הדרישה הקודמת‪.‬‬
‫‪Prototype-based Factory (64), Factory Method (73), Abstract Factory (72),‬‬
‫)‪Builder (77), Proxy (105‬‬
‫• איתלות באלגורית  נדרשת יכולת הרחבה או שינוי של אלגורית מסויי בעצ במהל ‬
‫הפיתוח‪ .‬כמו כ לעיתי אלגורית מסויי של העצ צרי להקבע באופ דינמי‪.‬‬
‫)‪Strategy (245), Template Method (240), Visitor (232), Iterator (209‬‬
‫• איתלות בי אופ ייצוג נתוני לבי הצגת  הפרדה בי מודל הנתוני של היישו לבי‬
‫ההצגה הויזואלית שלה מפשטת את תהלי הפיתוח‪.‬‬
‫)‪MVC (180‬‬
19 ‫ מבוא‬:1 ‫פרק‬

Reference
[Alexander79] The Timeless Way of Building, Christopher Alexander,
Oxford University Press, 1979.
[Booch97] Object Oriented Analysis and Design with Applications,
Grady Booch ,Benjamin Cummings, 1997
‫ ובכ הוא משמש‬,‫הספר כולל טכניקות תיכו מונחה עצמי כלליות‬
.[Gamma 1994] ‫כמשלי ל‬
[Booch99] The Unified Modeling Language User Guide, Grady Booch,
James Rumbaugh, Ivar Jacobson, Addison Wesley, 1999
‫ החומר‬.‫ מאת האבות היוצרי של תק זה‬,UML ‫ספר מקי ללימוד‬
‫ ע תרשימי רבי ודוגמאות ממערכות‬,‫מובא בצורה שוטפת וברורה‬
.‫עדכניות‬
[Gamma95] Design Patterns: Elements of Reusable Object-Oriented
Software, Erich Gamma, Richard Helm, Ralph Johnson, and
John Vlissides. (GOF), Addison Wesley 1995.
‫ התוכ ברור ומובא בצורה‬.Design Patterns ‫המקור הרשמי לנושאי‬
‫ מוסכמות הסימוני והדוגמאות‬,‫ עקב גילו של הספר‬.‫נוחה לקריאה‬
.‫המובאות בו לא עדכניות‬
[Meyers98] Effective C++, 2nd Edition, Scott Meyers, Addison Wesley,
1998
[Meyers96] More Effective C++, Scott Meyers, Addison Wesley, 1996
[MH-C++02] ‫ שנת‬,(2000) ‫ מרכז ההדרכה עיט‬,‫ מאיר סלע‬,‫
מדרי מקצועי‬C++
2002
.‫ בפרט ותיכו מונחה עצמי בכלל‬C++ ‫ספר מקי ומקצועי בנושא שפת‬
.‫ העדכנית‬C++ ‫ספר זה שבהוצאתנו כולל תיאור של כלל מרכיבי שפת‬
[Strous94] The design and evolution of C++, Bjarne Stroustrup , Addison
Wesley, 1994
‫ הוא עדיי משמש כאחד המקורות המרהיבי‬,‫למרות גילו של הספר‬
‫ מביא את‬,‫ שהוא ג יוצר השפה‬,‫ המחבר‬.C++ ‫להבנת מבנה שפת‬
‫השיקולי השוני שעמדו בבחירת מנגנוני מסוימי לשפה ובדחיית‬
.‫ בפרט ובתוכנה בכלל‬C++ ‫ ובכ מרחיב את הבנת הקורא ב‬, ‫אחרי‬
Design Patterns 20

[Strous97] The C++ Programming Language, 3rd Edition, Bjarne


Stroustrup , Addison Wesley, 1997
‫ הספר מקי מאוד‬.‫ מאת יוצר השפה‬,C++ ‫ספר התנ" לשפת התכנות‬
.‫ לעיתי א על חשבו הדידקטיות‬,‫ומעמיק מאוד באופ בלתי מתפשר‬
.‫התוכ מצטיי בניסוח מדויק וזהיר‬
[Vlissid98] Pattern Hatching, Design Patterns Applied, John Vlissides,
Addison Wesley 1998.
‫‪51‬‬ ‫פרק ‪ patterns :3‬בייצור עצמי‬

‫‪ patterns .3‬בייצור‬
‫עצמי‬
‫ה ‪ patterns‬שבפרק זה עוסקי בייצור של עצמי ‪ ,‬עבור מקרי בה משימה זו אינה‬
‫טריוויאלית‪ ,‬או שצריכה להתבצע תחת מגבלות מסוימות‪.‬‬
‫• ‪  Singleton‬הגבלת מספר העצמי המיוצרי ממחלקה מסוימת בתכנית ל ‪ ,1‬ומת גישה‬
‫גלובלית לעצ היחיד‪.‬‬
‫• ‪  Prototype‬ייצור פולימורפי של עצ עפ"י אבטיפוס )‪ (prototype‬נתו ע"י שיבוט‪.‬‬
‫• ‪  Prototype-based Factory‬ייצור פולימורפי של עצ עפ"י מפתח‪ ,‬תו שימוש ב‬
‫‪.Prototype‬‬

‫כמו כ נסקור בקצרה מספר ‪ patterns‬סטנדרטיי נוספי ‪:‬‬


‫• ‪  Abstract Factory‬ממשק לייצור משפחות עצמי ללא ציו הטיפוס הקונקרטי שלה ‪.‬‬
‫• ‪  Factory Method‬פונקציה לייצור עצ המוגדרת במחלקת הבסיס‪ ,‬כשהמחלקות הנגזרות‬
‫מחליטות איזה עצ בדיוק לייצר‪.‬‬
‫• ‪  Builder‬הפרדת הייצור של עצ מורכב מהייצוג שלו‪ ,‬כ שאותו תהלי ייצור יאפשר לבנות‬
‫ייצוגי שוני של העצ ‪.‬‬

‫כסיכו לסעי זה נראה כיצד נית לממש ‪ Serialization‬במערכת מונחית עצמי תו שימוש ב‬
‫‪ patterns‬הנ"ל‪.‬‬
Design Patterns 52

Singleton
‫בעיה‬
‫ שירותי ניהול‬, ‫ שירותי שעו‬: ‫ המספקת שירותי מערכת שוני‬System ‫נתונה מחלקת מערכת‬
:‫ וכ קבלת מידע על תכונות המערכת הנוכחית‬,(DLL) ‫ טעינת ספריות דינמיות‬, ‫זיכרו‬

System
-properties
+System()
+time_service()
+mem_service()
+load_library()
+get_property()

:‫קוד המחלקה‬
// system.h
#include <string>
#include <map>
using namespace std;

class System
{
public:
System() { properties["os"]="Windows"; properties["lang"]="Heb";
}
void time_service() { /*...*/ }
void mem_service() { /*...*/ }
void load_library (string lib) { /*...*/ }
string get_property(string name) const { return
properties[name];}
private:
map<string, string> properties;
};

‫דרישות‬
‫ אנחנו מעוניני לספק‬, ‫ כמו כ‬.‫ יהיה מופע אחד לכל היותר בתכנית‬System ‫אנו מעוניני שמ‬
: ‫ יש לשי לב לדרישות מהפתרו‬.‫גישה גלובלית לעצ היחיד של המחלקה‬
‫ שייתכ מצב בו לא‬,‫ משמעות הדבר‬.‫ יהיה בתכנית‬System ‫• לכל היותר עצ אחד מהמחלקה‬
.‫יהיה ג עצ אחד  במידה ולא נעשה שימוש בו בריצת התכנית‬
‫‪53‬‬ ‫פרק ‪ patterns :3‬בייצור עצמי‬

‫• הפתרו צרי להיות כפוי על כל קוד התכנית  כלומר‪ ,‬יש להבטיח שלא ייווצר יותר מעצ‬
‫יחיד בתכנית א לא כתוצאה משגיאה של מתכנת‪/‬ת‪.‬‬
‫• יש לאפשר גישה לעצ היחיד מכל מקו בתכנית‪ ,‬כלומר‪ ,‬הגישה לעצ צריכה להיות‬
‫גלובלית‪.‬‬

‫פתרו שגוי‬
‫פתרו ‪ :1‬הגדרת העצ כגלובלי‪ .‬נכריז על העצ בקוב ה ‪:.h‬‬
‫‪system.h:‬‬
‫>‪#include <string‬‬
‫>‪#include <map‬‬
‫;‪using namespace std‬‬

‫‪class System‬‬
‫{‬
‫‪...‬‬
‫;}‬

‫;‪extern System g_system‬‬ ‫‪// declaration of global system object‬‬

‫ונגדיר אותו בקוב המימוש‪:‬‬


‫‪system.cpp:‬‬
‫”‪#include “system.h‬‬

‫;‪System g_system‬‬ ‫‪// definition of global system object‬‬

‫קוד המשתמש יפעיל את השירותי של ‪ ,g_system‬למשל כ‪:‬‬


‫;)”‪g_system.load_library(“my_lib.dll‬‬

‫מה לא בסדר בפתרו זה?‬


‫‪ −‬אי מניעה של אפשרות ייצור עצמי נוספי מהמחלקה‪ .‬עמידות הפתרו נתונה‬
‫לחסדיו של קוד המשתמש  היא אינה נכפית עליו ע"י המהדר‪.‬‬
‫‪ −‬בדרישות הבעיה צויי שיש לאפשר ייצור עצ אחד לכל היותר‪ ,‬ולא לייצר אותו א‬
‫לא נעשה בו שימוש‪ .‬כא אנו מקצי את העצ תמיד‪.‬‬
‫‪" −‬זיהו" מרחב השמות הגלובלי  המשתנה ‪ g_system‬נוס למרחב השמות הגלובלי‪.‬‬
‫במערכות גדולות מאוד‪ ,‬קיימת הסתברות להתנגשות ע שמות זהי במרחב הגלובלי‪,‬‬
‫ונדרשות מוסכמות במת שמות בכדי למנוע זאת‪ .‬לדוגמא‪ ,‬נהוג להוסי לשמות‬
‫במרחב השמות הגלובלי ‪ 3‬אותיות המציינות את ש תתהמערכת לה משתיי הש‪.‬‬
‫‪Design Patterns 54‬‬

‫‪ −‬בעיה חמורה במיוחד עלולה לצו באיתחול המערכת‪ :‬נניח שבמערכת מספר עצמי‬
‫‪ Singleton‬כנ"ל  ‪  DB, Logger, Clock, System‬ובאיתחול שלה )ב‬
‫‪ (constructor‬ה תלויי הדדית עפ"י הגר הבא‪:‬‬

‫‪Clock‬‬

‫‪System‬‬

‫‪Logger‬‬

‫‪DB‬‬

‫לדוגמא‪ ,‬ח מ ‪ DB‬ל ‪ Logger‬מציי שב ‪ constructor‬של המחלקה ‪ DB‬עושי שימוש בעצ‬
‫ה ‪ .Logger‬לכ על העצ ‪ Logger‬להיווצר לפני יצירת ‪.DB‬‬
‫אול‪ ,‬מכיוו שב ‪ C/C++‬לא מוגדר סדר על יצירת העצמי הגלובליי )המוגדרי בקבצי‬
‫שוני(‪ ,‬מתעוררת פה בעיה איתחול חמורה‪ .‬הדרכי להתמודדות ע בעיה זו ה להגדיר את כל‬
‫העצמי הגלובליי בקוב מימוש אחד )ואז סדר היצירה הוא עפ"י סדר הגדרת(‪ ,‬או לבצע ‪2‬‬
‫פאזות של איתחול )‪.(double phase initialization‬‬

‫פתרו ‪ :2‬הגדרת כל ה ‪ members‬כסטטיי‪:‬‬


‫‪system.h:‬‬
‫>‪#include <string‬‬
‫>‪#include <map‬‬
‫;‪using namespace std‬‬

‫‪class System‬‬
‫{‬
‫‪public:‬‬
‫} ‪static void time_service() { /*...*/‬‬
‫} ‪static void mem_service() { /*...*/‬‬
‫} ‪static void load_library (string lib) { /*...*/‬‬
‫‪static string get_property(string name) const‬‬
‫};]‪{ return properties[name‬‬
‫‪private:‬‬
‫‪55‬‬ ‫פרק ‪ patterns :3‬בייצור עצמי‬

‫>‪static map<string, string‬‬ ‫;‪properties‬‬


‫;}‬

‫ובקוב המימוש יש להגדיר את המשתני הסטטיי של המחלקה‪:‬‬


‫‪system.cpp:‬‬
‫”‪#include “system.h‬‬

‫>‪map<string, string‬‬ ‫;‪System::properties‬‬

‫בפתרו זה  לעומת הקוד  מובטח לנו שלא יהיה נית לייצר יותר מהעתק אחד של ה‬
‫‪ members‬של המחלקה )ייתכ ומספר מתכנתי ייצרו מספר מופעי ל ‪ ,System‬א ה עדיי‬
‫יחלקו את אות ‪ ,members‬עקב היות סטטיי(‪.‬‬
‫כמו כ‪ ,‬מרחב השמות הגלובלי לא מזוה‪ ,‬מכיוו שאי צור בייצור עצ מהמחלקה  השירותי‬
‫שבה מופעלי בהקשר למחלקה‪ ,‬למשל כ‪:‬‬
‫;)”‪System::load_library(“my_lib.dll‬‬
‫מה לא תקי בפתרו זה?‬
‫‪ −‬כמו בפתרו הקוד‪ ,‬ג כא ה ‪ members‬מוקצי תמיד‪ ,‬ג א לא נעשה בה כל‬
‫שימוש בריצת היישו‪.‬‬
‫‪ −‬כמו בפתרו הקוד‪ ,‬סדר היצירה של עצמי סטטיי‪ ,‬כמו גלובליי‪ ,‬לא מוגדר ולכ‬
‫הבעיה שרירה וקיימת‪.‬‬

‫פתרו‬
‫המחלקה ‪ System‬תוגדר כ ‪ .Singleton‬היא תכלול‪:‬‬
‫‪ −‬פונקציה סטטית בש )(‪ instance‬המספקת עצ מהמחלקה‪ .‬עצ זה יוגדר כסטטי‬
‫בפונקציה‪.‬‬
‫‪ −‬ה ‪ constructor‬יוגדר כ ‪ protected‬בכדי למנוע ייצור עצמי ע"י קוד חיצוני‪ .‬מאותה‬
‫סיבה ג ה ‪ copy constructor‬יוכרז כ ‪) protected‬הוא לא מוגדר כ ‪ private‬בכדי‬
‫לאפשר ירושה מה ‪.(Singleton‬‬
Design Patterns 56

System
-properties
-System()
+time_service()
+mem_service()
+load_library()
+get_property()
+instance()

:C++ ‫קוד המחלקה ב‬


// system.h
#include <string>
#include <map>

using namespace std;

class System
{
public:
void time_service() { /*...*/ }
void mem_service() { /*...*/ }
void load_library (string lib) { /*...*/ }
string get_property(string name) const { return
properties[name]; }

// global point of access to the sole instance


static System& instance()
{
static System object;
return object;
}
protected:
System() { properties["os"]="Windows"; properties["lang"]="Heb";
}
System(const System&);
private:
map<string, string> properties;
};

‫ כלל אינו נדרש להגדרה  אול מכיוו שמסופק כזה‬copy constructor ‫יש לשי לב שה‬
‫ ולא ממשי אותו )היינו יכולי לממש אותו כפונקציה‬protected  ‫ מכריזי עליו כ‬,‫במחדל‬
‫ היתרו‬.‫ וג אז הוא גור לשגיאת הידור בנסיו של קוד חיצוני למחלקה לעשות בו שימוש‬,‫ריקה‬
‫ או מחלקות‬,‫שבאי מימושו הוא שכעת תתקבל שגיאת הידור ג בנסיו של קוד המחלקה עצמה‬
(!‫ לנסות לייצר העתקי של העצ‬,‫צאצאות‬
:‫קוד משתמש לדוגמא‬
#include <iostream>
#include "system.h"
using namespace std;
‫‪57‬‬ ‫פרק ‪ patterns :3‬בייצור עצמי‬

‫)(‪void main‬‬
‫{‬
‫;)(‪System::instance().time_service‬‬
‫;)(‪System::instance().mem_service‬‬
‫;)"‪System::instance().load_library("mylib.dll‬‬
‫<< " ‪cout << "Operating system:‬‬
‫;‪System::instance().get_property("os") << endl‬‬
‫)"‪cout << "Language: " << System::instance().get_property("lang‬‬
‫;‪<<endl‬‬

‫;‪System s1‬‬ ‫‪// error: private constructor‬‬


‫‪System s2(System::instance());// error: private copy constructor‬‬
‫}‬

‫הכללה‬
‫מגדירי מחלקה כ ‪ Singleton‬כאשר מעונייני לספק מופע אחד שלה‪ ,‬לכל היותר‪ ,‬בתכנית‪.‬‬
‫הגישה למופע זה היא גלובלית עבור כל מרכיבי התכנית‪:‬‬

‫‪Singleton‬‬

‫)(‪#Singleton‬‬
‫)(‪+operation1‬‬
‫)(‪+operation2‬‬
‫)(‪+operation3‬‬
‫)(‪+instance‬‬

‫‪ −‬פונקציה סטטית מספקת ‪ reference‬לעצ היחיד המוגדר בתוכה כסטטי‬


‫‪ −‬ה ‪ constructors‬מוגדרי כ ‪protected‬‬
‫• יתרונות‪:‬‬
‫‪ −‬ה ‪ Singleton‬מונע "זיהו" של מרחב השמות בכ שעצ המחלקה אינו מוגדר‬
‫במרחב השמות הגלובלי‪ .‬בכ למעשה ג מסופקת גישה תקנית לעצמי של‬
‫‪ :Singletons‬המשתמש תמיד ניגש לעצמי ע"י הפונקציה )(‪.instance‬‬
‫‪ Singleton −‬זמי תמיד למשתמש בו‪ :‬בניגוד לעצמי גלובליי שעבור סדר הבנייה‬
‫אינו מוגדר בתכנית ‪  C++‬ולכ אינ יכולי להתייחס אחד לשני בשלבי ה‬
‫‪ constructors‬שלה  ה ‪ Singletons‬נוצרי עפ"י סדר השימוש בה‪ ,‬ולכ לא חלה‬
‫עליה מגבלת שימוש כלשהי‪.‬‬
‫‪Design Patterns 58‬‬

‫• חסרונות‪:‬‬
‫‪ −‬קשה לעשות שימוש ב ‪ Singleton‬בהיררכיות ירושה‪ .‬כלומר‪ ,‬יש קושי עבור מחלקת‬
‫ה ‪ Singleton‬לרשת ממחלקה אחרת‪ ,‬או לרשת ממחלקת ה ‪.Singleton‬‬
‫‪ −‬ה ‪ Singleton‬הוא יחיד פר יישו‪ ,‬הואיל והוא מוגדר כסטטי בקטע הנתוני‬
‫)‪ .(Data Segment‬לא נית להגדיר בטכניקה זו ‪ Singleton‬פר ‪.Thread‬‬
‫• וריאציות‪:‬‬
‫‪ −‬קיימת שיטה נוספת להגדרת ‪ :Singleton‬נית להגדיר מצביע לעצ היחיד כסטטי‬
‫במחלקה )‪ ,(static member‬ולייצר אותו בפע הראשונה שנקראת הפונקציה‬
‫הסטטית )(‪ .instance‬שיטה זו היא מעט יותר מסורבלת ובעלת חסרו משמעותי‪ :‬ה‬
‫‪ destructor‬של ה ‪ Singleton‬אינו נקרא בסו התכנית )ראה‪/‬י דיו ב ]‪[Vlissid98‬‬
‫בסעי ”‪ .(“To kill a Singleton‬בשפות כגו ‪ Java/C#‬הבעיה לא קיימת בזכות ה‬
‫‪.Garbage Collector‬‬

‫מימוש ‪ Singleton‬ב ‪Java‬‬


‫ב ‪ Java‬לא קיי מנגנו של משתני סטטיימקומיי‪ .‬לכ אפשרות המימוש היחידה היא זו‬
‫של ‪.static-member‬‬
‫דוגמא למימוש ‪ Singleton‬בשיטת ה ‪:static member‬‬
‫‪class Singleton‬‬
‫{‬
‫;‪private static Singleton m_instance‬‬ ‫‪// the singleton object‬‬

‫‪public static Singleton instance() // static access method‬‬


‫{‬
‫)‪if (m_instance == null‬‬
‫;)(‪m_instance = new Singleton‬‬
‫;‪return m_instance‬‬
‫}‬
‫}‪protected Singleton() {...‬‬ ‫‪// protected constructor‬‬

‫…‬ ‫‪// rest of the class‬‬

‫}‬
‫• ‪ :Thread Safety‬במערכת מרובת ‪ ,Threads‬עלול להיווצר מצב שבו שני ‪) Threads‬או יותר(‬
‫יבצעו את שורת היצירה של ה‪ .Singleton‬זה יכול לקרות כאשר מבוצעת החלפת ‪Threads‬‬
‫מיד לאחר שורת הבדיקה‬
‫)‪if (m_instance == null‬‬
‫>>‪<<context switch‬‬
‫;)(‪m_instance = new Singleton‬‬
‫נית למנוע בעיה זו ע"י הוספת מנגנו סינכרו  נית להגדיר את קטע יצירת העצ כ‬
‫‪:synchronized‬‬
‫)(‪public static Singleton instance‬‬ ‫{‬
59 ‫ בייצור עצמי‬patterns :3 ‫פרק‬

synchronized(Singleton.class) { // protect critical code


if (m_instance == null)
m_instance = new Singleton();
}
return m_instance;
}
‫( מכיוו
שהפונקציה היא‬Singleton.class) ‫יש לשי לב לכ שההגנה מבוצעת בהקשר המחלקה‬
.‫ והיא נקראת בהקשר זה‬,‫סטטית‬
:‫• הערות‬

‫ הרי שנית‬java.io.Serializable ‫ מממשת את‬Singleton ‫ א מחלקת ה‬−


.deserialization ‫לייצר יותר מעצ אחד ממנה ע"י ביצוע חוזר של‬
‫ יכולות מחלקות צאצאות של‬,protected ‫ מוגדר כ‬constructor ‫ מכיוו
שה‬−
‫ לעקו את מגבלת הייצור‬package ‫ או מחלקות המשתייכות לאותו ה‬Singleton
‫ אול‬,private ‫ כ‬constructor ‫ נית
להגדיר את ה‬,‫ א רוצי למנוע זאת‬.‫שלו‬
.Singleton ‫בכ ג תמנע אפשרות ירושה מה‬

C# ‫ ב‬Singleton ‫מימוש‬
:‫ ע מעט הבדל במנגנו
הסינכרו
ובמודל השיקו‬,Java ‫ דומה לזה של‬C# ‫הקוד ב‬
class Singleton
{
protected static Singleton m_instance; // the singleton object

public static Singleton instance() // static access method


{
lock(typeof(Singleton)) { // protect critical code
if (m_instance == null)
m_instance = new Singleton();
}
return m_instance;
}
protected Singleton() {/*...*/} // protected constructor
}

‫ שמג‬,lock
‫ אחד ע"י מנגנו
הסינכרו‬Thread ‫ג כא
אנו מבצעי הגנה מפני גישה של יותר מ‬
.typeof(Singleton) ‫( המתקבל מ‬Type ‫ )מסוג‬Singleton ‫על טיפוס המחלקה של‬
‫‪Design Patterns 60‬‬

‫‪Prototype‬‬
‫בעיה‬
‫נתונה היררכית מחלקות הודעות‪:‬‬

‫»‪«interface‬‬
‫‪Message‬‬

‫)(‪+set‬‬
‫)(‪+print‬‬

‫‪Fax‬‬ ‫‪Mail‬‬ ‫‪Memo‬‬


‫‪-m_number‬‬ ‫‪-m_address‬‬ ‫‪-m_text‬‬
‫‪-m_image‬‬ ‫‪-m_text‬‬ ‫)(‪+set‬‬
‫)(‪+set‬‬ ‫)(‪+set‬‬ ‫)(‪+print‬‬
‫)(‪+print‬‬ ‫)(‪+print‬‬

‫הבעיה‪ :‬כיצד לייצר עצ כהעתק של עצ הנתו באופ פולימורפי‪.‬‬


‫למשל‪ ,‬הפונקציה הבאה מקבלת כפרמטר מצביע פולימורפי‪ ,‬והיא מעונינת לייצר העתק שלו‪:‬‬
‫)‪void f(Message *pm‬‬
‫{‬
‫= ‪Message *new_msg‬‬ ‫???‬
‫‪//...‬‬
‫}‬
‫מכיוו שלא ברור מהו הטיפוס המדויק של העצ המוצבע ע"י ‪ pm‬אי דר פשוטה לייצר עצ‬
‫דומה לו‪.‬‬
61 ‫ בייצור עצמי‬patterns :3 ‫פרק‬

‫פתרו‬
:‫ להיררכיה של ההודעות שמחזירה העתק של העצ‬clone() ‫נוסי פונקציה וירטואלית בש‬

«interface»
Message

+set()
+print()
+clone()

Fax Mail Memo


-m_number -m_address -m_text
-m_image -m_text +set()
+set() +set() +print()
+print() +print() +clone()
+clone() +clone()

‫ היא תוגדר כוירטואלית‬.‫ וירטואלי‬copy constructor ‫ היא למעשה מעי‬clone() ‫הפונקציה‬


:‫טהורה במחלקת הבסיס כ‬
class Message
{
public:
virtual ~Message() {}
virtual Message * clone() const = 0;
virtual void set(const string s1, const string s2) = 0;
virtual void print() const = 0;
};

‫ תדרוס המחלקה‬,‫ למשל‬,‫ כ‬.Message ‫יש לשי לב שהפונקציה מחזירה מצביע פולימורפי ל‬
:‫ את הפונקציה‬Fax
class Fax : public Message
{
public:
Fax() : m_number(0) {}
Fax(const string num, const string image) :
m_number(atol(num.c_str())), m_image(image){}
virtual Message* clone() const { return new Fax(*this); }
virtual void set(const string num, const string image)
{m_number=atol(num.c_str()); m_image=image;}
virtual void print() const { cout << " * Fax: num=" << m_number
<< " Image=" << m_image << "\n";}
private:
long m_number;
string m_image;
};
:‫ שלעיל‬f() ‫וכ תמומש הפונקציה‬
‫‪Design Patterns 62‬‬

‫)‪void f(Message *pm‬‬


‫{‬
‫= ‪Message *new_msg‬‬ ‫;)(‪pm->clone‬‬
‫‪// ...‬‬
‫}‬

‫הכללה‬
‫‪) Prototype‬הנקרא ג ”‪ (“Virtual Constructor‬הוא פתרו למצב בו צרי לייצר עצ בזמ‬
‫ריצה‪ ,‬שרק טיפוסו הפולימורפי ידוע בזמ הידור‪.‬‬
‫תרשי כללי‪:‬‬

‫‪Client‬‬ ‫‪Prototype‬‬
‫‪prototype‬‬

‫)(‪+operation‬‬ ‫)(‪+clone‬‬

‫)(‪p = prototype->clone‬‬ ‫‪ConcretePrototype1‬‬ ‫‪ConcretePrototype2‬‬

‫)(‪+clone‬‬ ‫)(‪+clone‬‬

‫‪return copy of self‬‬ ‫‪return copy of self‬‬

‫• יתרונות‪:‬‬
‫‪ −‬מודולריות‪ :‬קוד הלקוח אינו תלוי בטיפוסי המסוימי הנגזרי ממחלקת הבסיס ‬
‫הפונקציה )(‪ clone‬תחזיר תמיד העתק של העצ הנתו‪.‬‬
‫‪ −‬יעילות‪ :‬העתקת העצ מבוצעת באופ מיידי‪ ,‬בניגוד לשאילתת טיפוס הצורכת זמ‬
‫ומקו‪.‬‬
‫• חסרונות‪:‬‬
‫‪ −‬לא נית להעביר פרמטרי מטיפוסי שוני עפ"י העצ המיוצר לפונקציה )(‪,clone‬‬
‫הואיל והממשק שלה חייב להיות אחיד )פולימורפי(‪ .‬כתחלי‪ ,‬יש לספק פונקציה‬
‫לקביעת מצב העצ ))(‪ (set‬שתיקרא מייד לאחר העתקת העצ‪.‬‬
‫‪ −‬יש קושי בהחלטה כיצד להעתיק את העצ‪ :‬העתקה רדודה )‪ (Shallow Copy‬או‬
‫העתקה עמוקה )‪ .(Deep Copy‬א העצ כולל מצביעי‪ ,‬קיי קושי בהחלטה הא‬
‫לספק עצ החולק את המצביעי‪ ,‬או לספק עצ ע מידע כולל חדש‪.‬‬
‫‪63‬‬ ‫פרק ‪ patterns :3‬בייצור עצמי‬

‫בדר כלל מופעל ה ‪ copy constructor‬בפונקציה )(‪ ,clone‬והוא זה ש"מחליט" כיצד‬


‫להעתיק את העצ‪ ,‬אול נית להפעיל ג ‪ constructor‬מחדל‪ ,‬למשל‪.‬‬
‫• וריאציות ‪ Stroustrap :‬מציי בספרו ]‪ [Strous97‬במסגרת ה ‪ Relaxation Rules‬את‬
‫האפשרות )בהתא למימוש הקומפיילר( להחזיר במחלקה הנגזרת מצביע לטיפוס שלה‪,‬‬
‫כלומר‪:‬‬
‫‪class Base‬‬
‫{‬
‫‪...‬‬
‫;‪virtual Base * clone() const = 0‬‬
‫;}‬

‫‪class Derived : public Base‬‬


‫{‬
‫‪...‬‬
‫} ;)‪virtual Derived * clone() const { return new Derived(...‬‬
‫;}‬

‫זה יאפשר שימוש לא פולימורפי בפונקציה )(‪ Derived::clone‬ג ללא צור ב ‪ .casting‬לא‬
‫הרבה קומפיילרי תומכי באפשרות זו כיו‪.‬‬
‫• שימושי ידועי‪:‬‬
‫‪ Java −‬כוללת במחלקת הבסיס שלה‪ ,Object ,‬את השירות )(‪ clone‬המחזיר מצביע‬
‫מסוג ‪ Object‬לעצ הנוכחי‪ ,‬תו שימוש במנגנו השיקו‪ .‬בהיות ‪ Object‬בסיס לכל‬
‫המחלקות‪ ,‬בכ מסופק ממשק ‪ Prototype‬לכל עצמי ‪.Java‬‬
‫יש לשי לב שהמחלקות הנגזרות אינ צריכות לדרוס את הפונקציה )(‪  clone‬גירסת‬
‫הבסיס ‪ Object‬מבצעת את כל העבודה תו שימוש במודל השיקו ‪(Reflection‬‬
‫)‪ .Model‬מחלקת ‪ Java‬צריכה לממש את הממשק ‪ Cloneable‬בכדי לציי שהיא‬
‫מעוניינת לספק שירות ‪) cloning‬הממשק אינו כולל פונקציה כלשהי(‪.‬‬
‫‪ −‬באופ דומה ל ‪ ,Java‬ג ב ‪ C#‬קיימת תמיכה ב ‪ cloning‬כבר במחלקת הבסיס‪,‬‬
‫‪ .System.Object‬אול כא יש הבחנה בי העתקה רדודה )‪ (Shalow Copy‬לבי‬
‫העתקה עמוקה )‪:(Deep Copy‬‬
‫‪ MemberwiseClone() .1‬מבצעת העתקה רדודה‪ ,‬כאשר המצביעי בעצ‬
‫החדש בעלי ער זהה לאלו שבעצ המקור‪.‬‬
‫‪ : Clone() .2‬א עצ מסויי מעוניי לאפשר העתקה עמוקה שלו‪ ,‬עליו לממש‬
‫את הממשק ‪ IClonable‬תו מימוש הפונקציה )(‪ Clone‬שלו‪.‬‬
Design Patterns 64

Prototype-based Factory
‫בעיה‬
‫ לבחירת הודעה‬dialog ‫ נדרש להוסי לתכנית מחלקת‬,‫בהמש ליישו ההודעות מהסעי הקוד‬
:‫חדשה‬

NewMessageDialog «interface»
Message
+add_button()
+get_selected() +set()
+show() +print()
+run() +clone()

Fax Mail Memo


-m_number -m_address -m_text
-m_image -m_text +set()
+set() +set() +print()
+print() +print() +clone()
+clone() +clone()

‫ המחלקה‬.‫ לבחירת סוג הודעה חדשה‬dialog ‫ מייצגת תיבת‬NewMessageDialog ‫המחלקה‬


‫ לקבלת הבחירה של המשתמש  זה יכול להיות אינדקס או מחרוזת‬show() ‫כוללת פונקציה בש‬
.‫המתארת את טיפוס ההודעה‬
65 ‫ בייצור עצמי‬patterns :3 ‫פרק‬

:‫ מציגה למשתמש שלושה כפתורי לבחירת סוג הודעה חדשה שברצונו לשלוח‬dialog ‫תיבת ה‬

New Message

Fax

Choose type of
Mail message

Memo

?‫ כיצד לטפל בבחירת המשתמש ובייצור ההודעה החדשה‬:‫שאלה‬

‫פתרו שגוי‬
‫ ועל פי התוצאה נבצע את הקוד‬,show() ‫ על אינדקס ההודעה המוחזר מהפונקציה‬switch() ‫נבצע‬
:‫המתאי‬
#include "message.h"

class NewMessageDialog
{
...
int show(); // display dialog and return selected index
Message * run()
{
Message *pm;
int i = show(); // get message type index
switch(i)
{
case 0: // Fax
pm = new Fax;
break;
case 1: // Mail
pm = new Mail;
break;
case 2: // Memo
pm = new Memo;
break;
}
return pm;
}
};
‫‪Design Patterns 66‬‬

‫קוד משתמש לדוגמא‪:‬‬


‫)(‪int main‬‬
‫{‬
‫;‪NewMessageDialog dialog‬‬
‫;)(‪Message * p_new_msg = dialog.run‬‬

‫;)(‪p_new_msg->print‬‬

‫‪// use the new message‬‬


‫‪// ...‬‬

‫;‪delete p_new_msg‬‬

‫;‪return 0‬‬
‫}‬
‫לפתרו ע"י משפט ‪ switch‬שלושה חסרונות‪:‬‬
‫• הוא אינו יעיל  בכל בחירת משתמש יש לעבור על כל האפשרויות‪ ,‬ולבחור את הנכונה‪.‬‬
‫במקרה זה אמנ מעט אפשרויות )‪ ,(3‬א במקרה הכללי‪ ,‬כאשר יש ‪ n‬אפשרויות‪ ,‬בממוצע‬
‫יבוצע חיפוש על ‪ n/2‬אפשרויות‪ ,‬כלומר בסיבוכיות )‪.O(n‬‬
‫• הוא אינו מודולרי  קוד הלקוח תלוי בטיפוסי ההודעות הקיימי במערכת וצרי להתעדכ‬
‫בכל שינוי או תוספת של הודעה חדשה‪ .‬בנוס‪ ,‬עלולה להיווצר שגיאה בהתאמת האינדקס‬
‫הנבחר לסוגי ההודעות‪.‬‬
‫• הוא אינו דינמי  לא נית להוסי או לבטל אפשרויות נוספות בזמ ריצה‪.‬‬
67 ‫ בייצור עצמי‬patterns :3 ‫פרק‬

‫פתרו‬
:‫הפתרו כולל שני שלבי עקרוניי‬
‫ שמחזירה העתק של‬clone() ‫  נעשה שימוש בפונקציה הוירטואלית‬Prototype ‫ שימוש ב‬.1
‫ המחזירה את ש הטיפוס של‬get_type() ‫ נוסי פונקציה וירטואלית נוספת בש‬, ‫ כמו כ‬.‫העצ‬
:‫ההודעה‬

«interface»
Message

+set()
+print()
+clone()
+get_type()

Fax Mail Memo


-m_number -m_address -m_text
-m_image -m_text +set()
+set() +set() +print()
+print() +print() +clone()
+clone() +clone() +get_type()
+get_tpe() +get_type()

:‫שתי הפונקציות מוגדרות כוירטואליות טהורות במחלקת הבסיס כ‬


class Message
{
public:
virtual ~Message() {}
virtual Message* clone() const = 0;
virtual string get_type() const = 0;
virtual void set(const string s1, const string s2) = 0;
virtual void print() const = 0;
};

:‫ את הפונקציות‬Mail ‫ תדרוס המחלקה‬,‫ למשל‬,‫כ‬


class Mail : public Message
{
public:
Mail() {}
Mail(const string addr, const string text) : m_address(addr),
m_text(text) {}
virtual Message* clone() const { return new Mail(*this); }
virtual string get_type() const { return "Mail";}
virtual void set(const string addr, const string text)
Design Patterns 68

{m_address=addr; m_text=text;}
virtual void print() const { cout << " * Mail: address=" <<
m_address << ", text=" << m_text << "\n";}
private:
string m_address;
string m_text;
};

 Message ‫  מצביעי לעצמי‬Prototypes ‫ שתחזיק טבלה של‬Factory ‫ נגדיר מחלקה בש‬.2


‫ יפנה‬dialog ‫ וה‬,Singleton ‫ מחלקה זו תוגדר כ‬.‫עפ"י מחרוזות המתארות את סוג ההודעה‬
:‫אליה לייצור עצ עפ"י מחרוזת‬

NewMessageDialog

+add_button()
+get_selected()
+show()
+run() Message Factory
* 1 -map<string, Message*> m_prototypes
+set() +create_object()
+print() +add_proto()
m_prototypes
+clone()
+get_type()

Fax Mail Memo


-m_number -m_address -m_text
-m_image -m_text +set()
+set() +set() +print()
+print() +print() +clone()
+clone() +clone() +get_type()
+get_tpe() +get_type()

:Factory ‫קוד המחלקה‬


#include <map>
#include <string>
using namespace std;
#include "Message.h"
// Factory class - Creates Message Objects by their type name.
// - Singleton
class Factory
{
public:
void add(const Message *m) {m_prototypes[m->get_type()] = m;}
Message* create_object(const string &type)
{ return m_prototypes[type]->clone(); }
static Factory& instance() {// singleton access method
static Factory factory;
return factory;
}
protected:
69 ‫ בייצור עצמי‬patterns :3 ‫פרק‬

Factory() {}
Factory(const Factory &);
~Factory() {
for(map<const string, const Message *>::iterator i=
m_prototypes.begin(); i != m_prototypes.end(); i++)
delete i->second;
}
private:
map<const string, const Message *> m_prototypes;
};

(cloning) ‫ יבוצע שיבוט‬,‫ תציג את ההודעות ובבחירת המשתמש בכפתור מסוי‬dialog ‫תיבת ה‬
:‫של העצ המתאי בטבלה‬
class NewMessageDialog
{
public:
void add_button(string name) {}
string show();
Message * run()
{
string sel = show();
return Factory::instance().create_object(sel);
}
};
:Factory ‫פונקציה לרישו טיפוסי ההודעות ב‬
void register_prototypes()
{
Factory::instance().add_proto("Fax", new Fax);
Factory::instance().add_proto("Mail", new Mail);
Factory::instance().add_proto("Memo", new Memo);
}
.‫ לדוגמא‬,(4 ‫ פרק‬,‫ )להל‬Facade ‫פונקציה זו יכולה להיות גלובלית או במסגרת ה‬
:‫קוד תכנית הלקוח‬
int main() {
register_prototypes();
NewMessageDialog dialog;
Message * p_new_msg = dialog.run();

// use the new message...

delete p_new_msg;
return 0;
}
‫‪Design Patterns 70‬‬

‫כפי שנית לראות‪ ,‬מבנה מחלקת ה ‪ dialog‬הוא כעת‪:‬‬


‫• יעיל  קוד הפונקציה )(‪ run‬הינו קצר‪ ,‬ויצירת עצ ההודעה החדש מבוצעת בסיבוכיות ‪O(lg‬‬
‫)‪  n‬סיבוכיות חיפוש בטבלה ‪.map‬‬
‫• מודולרי  לא קיימת תלות בי פונקצית ההצגה של מחלקת ה ‪ dialog‬לבי טיפוסי ההודעה‪.‬‬
‫למשל‪ ,‬אי צור לעדכ פונקציה זו בהוספת סוג הודעה חדש‪.‬‬
‫• דינמי  נית לקבוע בזמ ריצה אילו טיפוסי הודעה זמיני ע"י ה ‪ ,Factory‬כלומר‪ ,‬אפשר‬
‫להוסי ל ‪ Factory‬עצמי הודעה מטיפוסי חדשי או להסיר עצמי קיימי‪.‬‬
‫כמו כ‪ ,‬מכיוו שהכפתורי מאותחלי עפ"י וקטור ה ‪ ,prototypes‬אי חשש לבילבול בי‬
‫שמות טיפוסי ההודעה לבי עצמי ההודעה עצמ‪.‬‬

‫הכללה‬
‫‪ Prototype-based Factory‬הוא מנגנו לייצור עצמי פולימורפי‪ ,‬תו הפרדה בי קוד הלקוח‬
‫לקוד העצ המיוצר‪ :‬הוא מאפשר לקוד לקוח לייצר עצמי מבלי לציי את הטיפוס המדוייק של‬
‫העצ‪ ,‬אלא רק מפתח שלו‪.‬‬

‫‪Client‬‬
‫‪Factory‬‬
‫»‪«Prototype‬‬ ‫*‬ ‫‪1‬‬
‫‪AbstractProduct‬‬
‫)(‪+create_object‬‬
‫)(‪+add_prototype‬‬
‫)(‪+clone‬‬ ‫‪m_prototypes‬‬

‫‪ProductA‬‬ ‫‪ProductB‬‬

‫)(‪+clone‬‬ ‫)(‪+clone‬‬

‫• יתרונות‪:‬‬
‫‪ −‬יעילות‪ :‬ה ‪ Factory‬מייצר עצ עפ"י מפתח בסיבוכיות של )‪ ,O(lg n‬כאשר ‪ n‬הוא‬
‫מספר ה ‪ prototypes‬האפשרי‪.‬‬
‫‪ −‬מודולריות‪ :‬ה קוד הלקוח וה ה ‪ Factory‬עצמו מבודדי מהטיפוסי המדוייקי‬
‫של העצמי ואינ תלויי בה‪.‬‬
‫‪ −‬דינמיות ‪ :‬נית לקבוע בזמ ריצה אילו טיפוסי יהיו ניתני לייצור בהקשר הנתו‪.‬‬
71 ‫ בייצור עצמי‬patterns :3 ‫פרק‬

‫ אינה מצריכה א לא הידור מחודש של‬Prototype ‫יש לשי לב לכ שתוספת טיפוס‬
‫( אינה תלויה א היא‬dialog ‫ ה‬,‫ המחלקה המשתמשת )בדוגמא הנ"ל‬.Factory ‫ה‬
.Prototypes ‫בטיפוסי ה‬
:‫• וריאציות‬
‫ של מחלקת‬RTTI ‫ מזהה מספרי או עצ‬,‫ יכול להיות מחרוזת‬prototypes ‫ מפתח ה‬−
.Prototype ‫ה‬
.‫ לשימוש כללי‬Prototype-based Factory ‫ של‬template ‫ נית להגדיר‬C++ ‫ ב‬−
‫ לטיפוסי מפתח ו‬C++ ‫ ב‬template ‫ כ‬Prototype-based Factory :‫דוגמא‬
:‫ כלליי‬prototype

#ifndef FACTORY_H
#define FACTORY_H
#include <map>
using namespace std;

// Factory class - Creates Objects by a key


// - Singleton
template <class Key, class Object>
class Factory
{
public:
void add_proto(const Key key, const Object *proto) {
m_prototypes[key] = proto;
}
Object* create_object(const Key &key) {
return m_prototypes[key]->clone();
}
static Factory& instance() { // singleton access method
static Factory factory;
return factory;
}
~Factory() {
for(map<const Key, const Object *>::iterator i=
m_prototypes.begin(); i != m_prototypes.end(); i++)
delete i->second;
}
private:
map<const Key, const Object *> m_prototypes;
Factory() {}
Factory(const Factory &);
Factory& operator=(const Factory&);
};
#endif

You might also like