✕ סגור 
צור קשר
תודה על ההתעניינות .

Thank you! Your submission has been received!

Oops! Something went wrong while submitting the form

ממשיכים עם פרידגמות התכנות - מגיעים לסיכום

ליאור בר-און
|
Apr 30, 2019
alt="blogs"
Event
alt="blogs"
alt="blogs"
title="Google"
Events

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

‍

אז מה הפרדיגמות המוקדמות תרמו לנו?

‍

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

יהיה קשה לדמיין אפילו - עבודה בלי חלק הפיתוחים שהן הוסיפו.

‍

‍

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

למשל: המצאת ה"בלוק". בתחילה - לא היה דבר כזה.

למשל: משפטי if היו יכולים באותה התקופה להפעיל רק ביטוי (expression) יחיד. לו היו 4 פעולות שלא היינו רוצים שיפעלו אם x הוא שלילי - היה עלינו לכתוב 4 משפטי if שכל אחד חוזר על הבדיקה "האם x שלילי" ואז מבצע עוד פעולה.

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

נסו לדמיין אותנו עובדים כך היום - עד כמה מסורבל וקשה זה!

דוגמה אחרת היא משפטי ה-GOTO המפורסמים. תוכנה היא לא תמיד רצף סדרתי של פעולות: לפעמים רוצים לקחת דרך אחרת. למעבר יש יכולת לעשות מעבר ולהריץ קוד ממקום אחר בזיכרון, התרגום הישיר של היכולת הזו היא פקודת GOTO (או JMP - אם אתם חושבים באסמבלי). יישום ראשוני של GOTO היה מעין תחליף פרימיטיבי לפונקציות: לך לשורה 300, ואז שורה 306 מחזירה אותנו לשורה 28 ממנה באנו. שימושים נוספים היו טיפול בשגיאות וניקוי זיכרון - מה שבשפות מודרניות כבר זוכה לטיפול ראוי יותר.

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

סיזיפי? סיזיפי אך אפשרי? - כך נראתה החלוציות בתחום התוכנה...

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

ארצה להדגיש שני עקרונות שצצו מהגישה הזו ושווים דיון, גם ממרום שנת 2019:

‍

Scoping [ב]

‍

הרעיון אומר כך: כאשר אנו נותנים שם (name binding) למשתנה, פונקציה, קבוע, וכו' - נאפשר להגביל את מרחב הקוד שיהיה חשוף לשם הזה.

בתחילה היה קיים רק המרחב הגלובלי, אך בשל התנגשויות / טעויות שנבעו מכך - החלו להגדיר scopes מצומצמים יותר.

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

בצורה כוללנית יותר, הרעיון אומר שננסה לצמצם את ההיכרויות במערכת - למינימום האפשרי.

כל אלמנט שאנו מגדירים (משתנה, מחלקה, וכו') - נגדיר ב-scope המצומצם ביותר שאפשר.

למשל: אם יש Enum שזקוקים לו רק במחלקה מסוימת - סגרו אותו ב-scope של המחלקה - ואל תחשפו אותו לשאר העולם.

היום, בעולם ה-IDE המתקדם - המשמעות המיידית של scoping היא מה יציע לנו ה-IDE ב-Auto-complete. אם נגדיר הכל ב-Scope הגלובלי - כל הקלדה תציע לנו את כל מה שיש.

אם נקפיד על הגדרת ישויות ב-scopes מצומצמים ככל הניתן - הצעות ה-Auto-complete יהפכו לממוקדות, והרבה יותר רלוונטיות.

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

מתלבטים מה עדיף? אני לא.

שווה לציין ש-Scoping הוא צעד ראשון בכיוון של Information hiding ו-Encapsulation. שוב ושוב עלו, לאורך ההיסטוריה - רעיונות בכיוון הזה.

‍

‍

Single Exit Principle

‍

בכדי להגדיר מהו מבנה צפוי ומנוהל של רצף הרצת התוכנה, ולהפסיק "לקפוץ לשורה אחרת בקוד", הגדירו בפרדיגמת התכנות המובנה את "עיקרון היציאה האחידה" (מפונקציה). שם נוסף:"Single Entry, Single Exit". לכל בלוק קוד (=פונקציה) צריך להיות רק מקום אחד להיכנס דרכו - ורק מקום אחד שניתן לצאת ממנו.

העיקרון הזה הוא מובנה בשפות שאנו עובדים איתן - עם כמה חריגות. קשה מאוד להסתדר ב-100% מהמקרים עם Single Exit יחיד.

נסו לרגע לחשוב: האם אתם מצליחים לחשוב היכן בשפה שלכם מפרים את העיקרון?

הנה דוגמאות עיקריות:

• Exceptions שנזרקים - הם לא Single Exit.
o continue או break בלולאה ל Label (בג'אווה, למשל) - הם לא Single Exit.
• אפשר גם לטעון ש-break ו-continue באופן כללי - הם לא Single Exit, לא ברמת הבלוק.
• בשפות פונקציונליות - לעתים אין להם מקבילה.
• coroutines (על עוד לא ממתינים באותו הבלוק לסיומם) - הם גם לא Single Exit. הפעלנו קוד - שרק בונה-עולם יודע מתי בדיוק הוא יסתיים.
• אחרון וחביב: יש גם מי שרואים בקריאת return מתוך גוף הפונקציה - הפרה של עקרון ה-Single Exit. אמנם נקודת היציאה נתונה וקבועה - אך דילגנו על קוד - להגיע אליה. הטענה העיקרית - היא שזה לא צפוי. אני מצפה שהפונקציה תגיע תמיד לשורתה האחרונה.

אין לי פה איזה המלצה או מסר גורף לגבי רעיון ה-Single Exist - אבל אני חושב ששווה להכיר אותו.

אני, אישית, שמח שיש break ו-continue בלולאות. אני מרגיש נוח להשתמש ב-return מתוך גוף של פונקציה (יש לזה מחיר מסוים - אבל האלטרנטיבה פחות טובה לדעתי).

‍

סיכום

‍

שוב לא הצלחתי לסיים את הפוסט ב-400 מילים.

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

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

שיהיה לנו בהצלחה!

‍

‍
---

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

‍

‍

‍


מאת: ליאור בר-און, ארכיטקט ראשי בחברת Gett וכותב בבלוג http://www.softwarearchiblog.com

רוצים להתעדכן בתכנים נוספים בנושאי ענן וטכנולוגיות מתקדמות? הירשמו עכשיו לניוזלטר שלנו ותמיד תישארו בעניינים > להרשמה

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

‍

אז מה הפרדיגמות המוקדמות תרמו לנו?

‍

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

יהיה קשה לדמיין אפילו - עבודה בלי חלק הפיתוחים שהן הוסיפו.

‍

‍

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

למשל: המצאת ה"בלוק". בתחילה - לא היה דבר כזה.

למשל: משפטי if היו יכולים באותה התקופה להפעיל רק ביטוי (expression) יחיד. לו היו 4 פעולות שלא היינו רוצים שיפעלו אם x הוא שלילי - היה עלינו לכתוב 4 משפטי if שכל אחד חוזר על הבדיקה "האם x שלילי" ואז מבצע עוד פעולה.

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

נסו לדמיין אותנו עובדים כך היום - עד כמה מסורבל וקשה זה!

דוגמה אחרת היא משפטי ה-GOTO המפורסמים. תוכנה היא לא תמיד רצף סדרתי של פעולות: לפעמים רוצים לקחת דרך אחרת. למעבר יש יכולת לעשות מעבר ולהריץ קוד ממקום אחר בזיכרון, התרגום הישיר של היכולת הזו היא פקודת GOTO (או JMP - אם אתם חושבים באסמבלי). יישום ראשוני של GOTO היה מעין תחליף פרימיטיבי לפונקציות: לך לשורה 300, ואז שורה 306 מחזירה אותנו לשורה 28 ממנה באנו. שימושים נוספים היו טיפול בשגיאות וניקוי זיכרון - מה שבשפות מודרניות כבר זוכה לטיפול ראוי יותר.

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

סיזיפי? סיזיפי אך אפשרי? - כך נראתה החלוציות בתחום התוכנה...

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

ארצה להדגיש שני עקרונות שצצו מהגישה הזו ושווים דיון, גם ממרום שנת 2019:

‍

Scoping [ב]

‍

הרעיון אומר כך: כאשר אנו נותנים שם (name binding) למשתנה, פונקציה, קבוע, וכו' - נאפשר להגביל את מרחב הקוד שיהיה חשוף לשם הזה.

בתחילה היה קיים רק המרחב הגלובלי, אך בשל התנגשויות / טעויות שנבעו מכך - החלו להגדיר scopes מצומצמים יותר.

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

בצורה כוללנית יותר, הרעיון אומר שננסה לצמצם את ההיכרויות במערכת - למינימום האפשרי.

כל אלמנט שאנו מגדירים (משתנה, מחלקה, וכו') - נגדיר ב-scope המצומצם ביותר שאפשר.

למשל: אם יש Enum שזקוקים לו רק במחלקה מסוימת - סגרו אותו ב-scope של המחלקה - ואל תחשפו אותו לשאר העולם.

היום, בעולם ה-IDE המתקדם - המשמעות המיידית של scoping היא מה יציע לנו ה-IDE ב-Auto-complete. אם נגדיר הכל ב-Scope הגלובלי - כל הקלדה תציע לנו את כל מה שיש.

אם נקפיד על הגדרת ישויות ב-scopes מצומצמים ככל הניתן - הצעות ה-Auto-complete יהפכו לממוקדות, והרבה יותר רלוונטיות.

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

מתלבטים מה עדיף? אני לא.

שווה לציין ש-Scoping הוא צעד ראשון בכיוון של Information hiding ו-Encapsulation. שוב ושוב עלו, לאורך ההיסטוריה - רעיונות בכיוון הזה.

‍

‍

Single Exit Principle

‍

בכדי להגדיר מהו מבנה צפוי ומנוהל של רצף הרצת התוכנה, ולהפסיק "לקפוץ לשורה אחרת בקוד", הגדירו בפרדיגמת התכנות המובנה את "עיקרון היציאה האחידה" (מפונקציה). שם נוסף:"Single Entry, Single Exit". לכל בלוק קוד (=פונקציה) צריך להיות רק מקום אחד להיכנס דרכו - ורק מקום אחד שניתן לצאת ממנו.

העיקרון הזה הוא מובנה בשפות שאנו עובדים איתן - עם כמה חריגות. קשה מאוד להסתדר ב-100% מהמקרים עם Single Exit יחיד.

נסו לרגע לחשוב: האם אתם מצליחים לחשוב היכן בשפה שלכם מפרים את העיקרון?

הנה דוגמאות עיקריות:

• Exceptions שנזרקים - הם לא Single Exit.
o continue או break בלולאה ל Label (בג'אווה, למשל) - הם לא Single Exit.
• אפשר גם לטעון ש-break ו-continue באופן כללי - הם לא Single Exit, לא ברמת הבלוק.
• בשפות פונקציונליות - לעתים אין להם מקבילה.
• coroutines (על עוד לא ממתינים באותו הבלוק לסיומם) - הם גם לא Single Exit. הפעלנו קוד - שרק בונה-עולם יודע מתי בדיוק הוא יסתיים.
• אחרון וחביב: יש גם מי שרואים בקריאת return מתוך גוף הפונקציה - הפרה של עקרון ה-Single Exit. אמנם נקודת היציאה נתונה וקבועה - אך דילגנו על קוד - להגיע אליה. הטענה העיקרית - היא שזה לא צפוי. אני מצפה שהפונקציה תגיע תמיד לשורתה האחרונה.

אין לי פה איזה המלצה או מסר גורף לגבי רעיון ה-Single Exist - אבל אני חושב ששווה להכיר אותו.

אני, אישית, שמח שיש break ו-continue בלולאות. אני מרגיש נוח להשתמש ב-return מתוך גוף של פונקציה (יש לזה מחיר מסוים - אבל האלטרנטיבה פחות טובה לדעתי).

‍

סיכום

‍

שוב לא הצלחתי לסיים את הפוסט ב-400 מילים.

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

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

שיהיה לנו בהצלחה!

‍

‍
---

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

‍

‍

‍


מאת: ליאור בר-און, ארכיטקט ראשי בחברת Gett וכותב בבלוג http://www.softwarearchiblog.com

רוצים להתעדכן בתכנים נוספים בנושאי ענן וטכנולוגיות מתקדמות? הירשמו עכשיו לניוזלטר שלנו ותמיד תישארו בעניינים > להרשמה

לפרטים נוספים ויצירת קשר עם נציג אורקל

תודה הודעתך התקבלה

הודעתך לא התקבלה - נסה שוב מאוחר יותר

ליאור בר-און

הירשם לרשימת הדיוור של IsraelClouds

Thank you! Your submission has been received!

Oops! Something went wrong while submitting the form

מילון מונחיםהשירותים שלנו תנאי שימושהרשמה לניוזלטרמדיניות פרטיות