פרק קודם:
פייתון 18 - מחלקות חלק במה נלמד
- עיצוב מונחה עצמים
- עקרונות עיצוב בסיסיים
- אבסטרקציה
- הכלה מול ירושה
- KISS
- לזהות מה משתנה ומה לא
- עקרון הוליווד
עיצוב תכנות מונחה עצמים
כמו בציור שהאלמנטים הבסיסיים שלנו הם צבעים, בתכנות מונחה עצמים האלמנט הבסיסי שלנו הוא המחלקה!
מחלקות מורכבות ממידע והתנהגות.
על מנת לעצב מערכת בעזרת אובייקטים אנחנו צריכים להתרגל לחשוב בעזרת אובייקטים.
דוגמא שהולכת איתי שנים רבות - ככה אני למדתי תכנות מונחה עצמים - היא לעצב מערכת בקרה לאבטחת בתים.
עקרון 1 - לחשוב באבסטרקציה
יש שני דרכי חשיבה כאשר מסתכלים על מערכת - הרכיבים הספציפיים או הרעיונות מאחורי הרכיבים.
מתכנת צעיר בדרך כלל יחשוב בדרכים ספציפיים - למערכת יש חיישני חום, חיישני תנועה, בקר לכל חדר וכדו’…
מתכנת יותר בוגר ייחשוב על הקונספטים מאחורי המערכת - בתים מסודרים בחדרים, לכל חדר יש “רמת אבטחה” מסוימת שכוללת מכשור שונה.
בכללי רמת האבסטרקציה והמורכבות עולים - ומתכנת מנוסה ידע איך לנהל את המורכבות כך שתהיה פשוטה יותר.
למשל, ניקח רעיון כללי - חיישנים
ואז נחשוב על חיישנים ספציפיים:
- חום
- תנועה
- לחצנים
- מזלגות בתוך פחית מתכת
תעלו אסוציאציות נוספות באותו סגנון “רעיון” ו”דברים ספציפיים”.
הרעיון: להגיע ממקום א’ למקום ב’.
דברים ספציפיים:רכב
רכבת
אופניים
אוטובוס
הרעיון: דפדפן לגלישה באתרים
דברים ספציפיים:Chrome
FireFox
Safari
עקרון 2 - הכלה מול ירושה
מחלקות יכולות להיות בסדר שונה ומשונה - מחלקה א’ יכולה לרשת מחלקה ב’,
כמו כן מחלקה א’ יכולה להכיל את מחלקה ב’.
מה ההבדל?
- מחלקה ב’ היא סוג של מחלקה א’.
- למחלקה א’ יש מחלקה ב’.
למשל:
- יונדאי הוא רכב
- לרכב יונדאי יש גלגלים.
אזי:
- יודנאי יורש מ”רכב”.
- רכב יונדאי מכיל מחלקת גלגלים.
הכלה:
1 | class Engine: |
ירושה:
1 | class Engine: |
יש שני עקרונות כאן:
- יש לבחור בין הכלה לירושה - כאשר המחלקה היא מממשת התנהגות העיצוב הנכון הוא הכלה.
ניתן לבדוק זאת בעזרת המשפט:
“האם X הוא Y”
למשל - האם תפוח הוא פרי אזי זו ירושה.
לתפוח יש גרעינים - אזי זו הכלה. - פולימורפיזם עובד פה שעות נוספות - הכלה וירושה עובדים יד ביד.
בדוגמת המנוע ניתן לראות שהשתמשנו בהכלה של מנוע - והחלפת המנוע בסוג אחר של מנוע.
עקרון 3 - KISS - Keep it simple stupid
עקרון מאוד חשוב!
אחד הדברים המנחים אותי הוא ליצור קוד טוב - ולכתוב קוד מספיק טוב.
כבר תיארתי את זה בחלק מהמאמרים שלי, צריך להגדיר את המטרות שלנו לפני שאנחנו ניגשים לפתירת הבעיה.
בדרך הזו יהיה לנו את הכלים לכתוב קוד שלא משאיר מרוכבות מיותרת מאחורה.
בתור מתכנתים עלינו לנהל מורכבויות ולהפוך אותן לפשוטות יותר.
המטרה היא לכתוב קוד עובד ומובן - לא מסובך.
עוד מטרה של העקרון הזה הוא בעצם לצמצם את השימוש בעקרונות האחרים ולא להגזים איתם.
עקרון 4 - להפריד בין מה שמשתנה למה שלא משתנה
ההפרדה בין שינויי קוד הוא מה שהופך קוד פשוט לקוד מורכב.
העקרון הזה הוא אחד הכלים שלנו להוריד את המורכבות בקוד.
למשל אם יש לנו 3 סוגי מחלקות שמממשות את אותה הלוגיקה אך משהו מסוים משתנה:
1 | class Enemy: |
זה די קל לזהות כאן מה משתנה - כל אחת מהמתודות Execute
עושות משהו לפני שמתקיפות את השחקן.
לכן ניתן למגר את האסטרטגיה למשהו מסוים ולממש רק את האסטרטגיה בכל אחת מהמחלקות.
1 | class Enemy: |
הטכניקה כאן משתמשת בפולימורפיזם על מנת לייצא פונקציה שכל אחת מהמחלקות היורשות ממשמות אותה,
ככה אנחנו יצרנו קונספט “אסטרטגיה” במקום להתמקד בקוד מאוד ספציפי לכל מחלקה.
כך גם מיגרנו את הקוד שמשתנה - Strategize
והקוד שלא משתנה - פונקציית Attack
.
עקרון 5 - להוריד Coupling לעלות Cohesion
- Coupling - מדד לכמה שני יישויות תלויות אחת בשנייה.
- Cohesion - מדד לכמה שני יישויות קשורות אחת לשנייה.
Coupling
1 | class A: |
המחלקה B
תלויה במחלקה A
.
אם נשנה משהו ב-A
זה ישפיע על B
.
למשל אם נחליט לשנות את הפונקציה מהדפסה להחזרה של מחרוזת, נפגע במחלקת B
!
1 | class A: |
לכן העקרון בא ומגדיר - יש להוריד את התלות בין שני ישויות, למשל:
1 | class A: |
מחלקת B
כבר לא שומרת את A
בתוכה אלה מקבלת מחרוזת במקום זה.
Cohesion
1 | class Printer: |
מתוך כל המתודות של מחלקת Printer
, מהן הפונקציות שקשורות ומה לא?
מחלקת Printer
אחראית להדפסה.
מתוך כך ניתן לומר שהפונקציות SaveFile
ו-ChangeTitle
לא קשורות.
מחלקה שה-Cohesion שלה גבוה:
1 | class Printer: |
Cohesion מגדיר את הקשר הישיר בין מתודות או מחלקות.
כאשר מפצלים את הקוד ליחידות קטנות יותר צריך לשים לב כמה היחידות האלו קשורות אחת לשנייה.
אם אנחנו מפתחים מודול שקורא וכותב קבצים, אנחנו צריכים שהמודול יהיה אחראי על קבצים בלבד.
עקרון 5 - הוליווד - אל תקרא לי, אני אקרא לך
העקרון עוזר להפריד ישויות ולהוריד את ה-Coupling ביניהם.
למשל בקטע הקוד הבא:
1 | class Person: |
מה לא בסדר בו?
כדי לקרוא את החדשות האחרונות אנחנו מחכים ל-provider
שיהיה לו חדשות.
במקום זה - אנחנו צריכים ליצור תבנית שונה שמתארת “רושם-נרשם-ספק”.
ספק עיתונים שנותן לכל הנרשמים שלו את החדשות האחרונות.
לתבנית הזו יש גם שם - The Observable Pattern.
ככה זה נראה בקוד:
1 | class Person: |
מה היתרונות בתבנית הזו?
- מוריד Coupling בין מחלקות, ניתן לממש מחלקה שלישית כדי לנהל את התלות בין שני המחלקות.
- ניתן לשנות בקלות יותר את ה-
NewsPaperProvider
מבלי לחשוף יותר מדי מידע החוצה. - ניתן לממש צורות שונות של הרשמה כך לתת את המידע הרלוונטי רק לישויות שרוצות אותן.
- התבנית הזו תומכת בריבוי ישויות
- התבנית תומכת בהלכה במקום ירושה
חמשת העקרונות העיצוביים האלו ייעזרו לכם לנהל מורכבות קוד שונה ולדעת כיצד להפריד ישויות מבלי ליצור תלותיות יתר בינהן.
יש הרבה מה ללמוד בעיצוב תוכנה והרוב מגיע מלמידה והתנסות.
בדיוק בגלל זה חשוב מאוד להתעדכן ולקרוא כמה שיותר על קוד.
יש באתר מאמרים נוספים שמדברים על עיצוב נכון ותכנון נכון אז מומלץ למי שמתעניין ללכת ולקרוא אותם :)
הפרק הבא חוזר לקוד פייתוני ולפונקציות:
פייתון 20 - למבדות