תכנות מונחה עצמים | Open/Closed Principle
על מנת שהמערכות, היישומים והתשתיות שאנחנו מפתחים יהיו חזקים, יציבים וניתנים לתחזוקה ולשינויים, עלינו לדעת כיצד לעצב אותם בצורה הטובה ביותר.
העקרונות הבסיסיים של תכנות מונחה עצמים לרוב אינם יספיקו למפתח בכדי לכתוב מערכת ברמה גבוהה.
היום אני הולך להציג את עקרונות העיצוב SOLID. אלו הם 5 העקרונות הראשונים והחשובים ביותר בעיני של עיצוב תוכנה וקוד באמצעות תכנות מונחה עצמים.
בראייתי, כל עקרון נבנה על גבי העיקרון הקודם לו, והבנת עיקרון מסויים תהיה פשוטה בהרבה אם נבין את העקרונות הקודמים.
בראייתי, כל עקרון נבנה על גבי העיקרון הקודם לו, והבנת עיקרון מסויים תהיה פשוטה בהרבה אם נבין את העקרונות הקודמים.
המדריכים וההסברים נכתבו בC#, אך הם מתאימים למתכנתים בכל שפה משום שהכוונה כאן היא להציג עקרונות. לאחר שהבנו את העקרונות ניתן לממשם בכל שפת OOP.
תנאי קדם למדריך: היכרות עם תכנות מונחה עצמים.
מה זה SOLID?
S - single responsibility principle - SRP
O - open closed principle - OCP
L - Liskov substitution principle - LSP
I - interface segregation principle - ISP
D - Dependency injection principle - DIP
לא, אל תצפה להבין מראשי התיבות מה כל עיקרון אומר, ראשי התיבות ושמות כאן אך ורק כדאי להזכיר לנו אילו עקרונות קיימים.
בפוסטים הקרובים אסקור כל אחד מחמשת העקרונות הללו, אצלול לעומקו ואציג אותו באמצעות דוגמאות קוד מהחיים האמיתיים.
מהו Open/Closed Principle?
open/closed principle או בקיצור, OCP, אומר בגדול - השאר את הקוד שלך פתוח להרחבות אך סגור לשינויים.
OCP בנוי ברובו על עקרונות האבסטרקציה וההרושה של תכנות מונחה עצמים, ומומלץ מאוד להבין את המנגנון של הפשטה וירושה של מחלקות לפני שניגשים להבין את העיקרון.
והרי לנו פונקציה שעושה בדיוק את מה שאנחנו רוצים. והכי חשוב, הופכת את ג'ון הבוס הלחוץ שלנו, למרוצה.
עבר שבוע, וג'ון פנה אלינו בדרישה נוספת - "אני רוצה שהתשתית שלנו תתמוך גם במכשיר Pixel."
OCP בנוי ברובו על עקרונות האבסטרקציה וההרושה של תכנות מונחה עצמים, ומומלץ מאוד להבין את המנגנון של הפשטה וירושה של מחלקות לפני שניגשים להבין את העיקרון.
מימוש עיקרון Open/Closed Principle ב- C#
נניח ואנחנו מפתחי אוטומציה של חברת גוגל ועלינו לבנות מוצר בדיקות אוטומטי שיתמוך במגוון מכשירי אנדרואיד.
הגיע אלינו ג'ון, המנהל הנחמד שלנו, ואמר לנו - "אני רוצה שתבנה תשתית שתתמוך במכשיר Galaxy S8. הדרישה הראשית לתשתית כעת היא שיהיה ניתן לשלוט על המכשיר, ובאופן אוטומטי לפתוח את מסך הנעילה שלו ולהגיע למסך הבית".
באחד המימושים, ביקש ג'ון שנכתוב פעולה שמקבלת רשימת מכשירים, עוברת עליהם בלולאה ופותחת את מסך הנעילה בכל אחד.
איזה כיף, לכתוב פעולה שפותחת את מסך הנעילה ב Galaxy S8 אני כבר יודע, ולולאות foreach אני מכיר ממזמן. קלי קלות.
כל מה שאצטרך לעשות זה ליצור את המחלקות הבאות:
- GalaxyS8 - מחלקה שתכיל את מאפייני המכשיר
- DeviceController - מחלקה שתכיל את הפעולה שהתבקשה קודם לכן.
עבר שבוע, וג'ון פנה אלינו בדרישה נוספת - "אני רוצה שהתשתית שלנו תתמוך גם במכשיר Pixel."
הקושי: מסך הנעילה נפתח באופן שונה במכשיר Galaxy S8 מבמכשיר Pixel.
איך נתגבר על הקושי?
הדרך הלא נכונה:
נוסיף מחלקה בשם Pixel , והפעם נרוץ בלולאה על מערך Objects! הרי כל אובייקט יורש מ Object.
בעבור כל אובייקט נבדוק האם הוא Galaxy או Pixel ונבצע עבורו את המימוש המתאים.
כעת המחלקות Pixel ו- DeviceController יראו כך.
ניצור מחלקה אבסטרקטית בשם Device שמכילה פונקציה לא ממומשת (Abstract) בשם OpenScreenLock
הערה: כאשר אנו יורשים ממחלקה אבסטרקטית, עלינו לממש כל פעולה אבסטרקטית שכתובה במחלקת האב.
המחלקה Device תראה כך:
וכעת המחלקות Pixel ו- GalaxyS8 ירשו מהמחלקה האבסטרקטית, וכמובן יממשו את הפעולה OpenScreenLock, כל אחת בדרכה שלה.
בעבור כל אובייקט נבדוק האם הוא Galaxy או Pixel ונבצע עבורו את המימוש המתאים.
כעת המחלקות Pixel ו- DeviceController יראו כך.
מה הבעיה בדרך שהוצעה?
במידה ונרצה להוסיף עוד מכשיר שגם בו דרך פתיחת המסך שונה, נאלץ לגשת שוב למחלקה DeviceController ולהוסיף תנאי IF נוסף.
ואני שוב מזכיר מה Bertand Meyer אמר, אנחנו רוצים להשאיר את הקוד שלנו סגור לשינויים אך פתוח להרחבות.
אז איך לעזאזל עושים את זה?
כמו שאמרתי בהתחלה, מימוש העיקרון מתבסס על מנגנוני ההורשה והאבסטרקציה.ניצור מחלקה אבסטרקטית בשם Device שמכילה פונקציה לא ממומשת (Abstract) בשם OpenScreenLock
הערה: כאשר אנו יורשים ממחלקה אבסטרקטית, עלינו לממש כל פעולה אבסטרקטית שכתובה במחלקת האב.
המחלקה Device תראה כך:
וכעת המחלקות Pixel ו- GalaxyS8 ירשו מהמחלקה האבסטרקטית, וכמובן יממשו את הפעולה OpenScreenLock, כל אחת בדרכה שלה.
עכשיו, הפונקציונליות שביקש מאיתנו ג'ון המנהל שלנו לממש, תהיה נוחה, גנרית, והכי חשוב - סגורה לשינויים!
כל מה שיידרש כאן זה לעבור בלולאה על מערך של Device ולבצע בעבור כל אחד את הפעולה OpenScreenLock.
במהלך הלולאה הפעולה OpenScreenLock הרלוונטית תקרא - של Pixel, GalaxyS8 או כל Device אחר!
סיכום:
היום הצגתי את העיקרון מספר 2 של SOLID.
אם נתרכז במימוש נראה שהעיקרון מבוסס גם על העיקרון הראשון שהוצג - SRP.
מימוש של OCP בקוד שלנו יחסוך סרבול מיותר בקוד ויהפוך אותו להרבה יותר ססטיינבילי ונוח לתחזוקה.
פתחו את הקוד שלכם ודמיינו דרישות נוספות, דומות לדרישה שהוספתי בדוגמה.
האם הקוד שלכם בנוי בצורה בה כל שידרש מכם כדי לענות על הדרישה הוא להוסיף פעולה או מחלקה?
במילים אחרות, שימו לב שאינכם צריכים לגשת אל הקוד ולשנות אותו במקרה שדרישות נוספות יגיעו.
נתראה בפוסט הבא :)
תגובות
הוסף רשומת תגובה