CodeGym /בלוג Java /Random-HE /כללי קידוד: מיצירת מערכת ועד לעבודה עם אובייקטים
John Squirrels
רָמָה
San Francisco

כללי קידוד: מיצירת מערכת ועד לעבודה עם אובייקטים

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

מערכות

המאפיינים הבאים הם בדרך כלל מאפיינים רצויים של מערכת:
  • מורכבות מינימלית. יש להימנע מפרויקטים מסובכים מדי. הדבר החשוב ביותר הוא פשטות ובהירות (פשוט יותר = טוב יותר).
  • קלות תחזוקה. בעת יצירת אפליקציה, עליך לזכור כי יהיה צורך לתחזק אותה (גם אם אתה באופן אישי לא תהיה אחראי לתחזוקה). המשמעות היא שהקוד חייב להיות ברור וברור.
  • צימוד רופף. המשמעות היא שאנו ממזערים את מספר התלות בין חלקים שונים של התוכנית (ממקסמים את הציות שלנו לעקרונות OOP).
  • שימוש חוזר. אנו מתכננים את המערכת שלנו עם היכולת לעשות שימוש חוזר ברכיבים ביישומים אחרים.
  • הִטַלטְלוּת. זה צריך להיות קל להתאים מערכת לסביבה אחרת.
  • סגנון אחיד. אנו מעצבים את המערכת שלנו תוך שימוש בסגנון אחיד על מרכיביה השונים.
  • הרחבה (מדרגיות). אנו יכולים לשפר את המערכת מבלי להפר את המבנה הבסיסי שלה (הוספה או שינוי של רכיב לא אמורה להשפיע על כל האחרים).
זה כמעט בלתי אפשרי לבנות אפליקציה שאינה דורשת שינויים או פונקציונליות חדשה. נצטרך כל הזמן להוסיף חלקים חדשים כדי לעזור ליצירת המוח שלנו להתעדכן בזמנים. כאן נכנסת לתמונה המדרגיות. מדרגיות היא בעצם הרחבת האפליקציה, הוספת פונקציונליות חדשה ועבודה עם יותר משאבים (או, במילים אחרות, עם עומס גדול יותר). במילים אחרות, על מנת להקל על הוספת היגיון חדש, אנו מקפידים על כמה כללים, כגון הפחתת הצימוד של המערכת על ידי הגדלת המודולריות.כללי קידוד: מיצירת מערכת ועד לעבודה עם אובייקטים - 2

מקור תמונה

שלבי תכנון מערכת

  1. מערכת תוכנה. עיצוב האפליקציה הכוללת.
  2. חלוקה לתת-מערכות/חבילות. הגדירו חלקים נבדלים לוגית והגדירו את הכללים לאינטראקציה ביניהם.
  3. חלוקה של תת-מערכות למחלקות. חלקו חלקים מהמערכת למחלקות וממשקים ספציפיים, והגדירו את האינטראקציה ביניהם.
  4. חלוקת מחלקות לשיטות. צור הגדרה מלאה של השיטות הדרושות לכיתה, בהתבסס על האחריות שהוקצתה לה.
  5. עיצוב שיטה. צור הגדרה מפורטת של הפונקציונליות של שיטות בודדות.
בדרך כלל מפתחים רגילים מטפלים בתכנון זה, בעוד שאדריכל האפליקציה מטפל בנקודות שתוארו לעיל.

עקרונות ותפיסות כלליות של עיצוב מערכת

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

AOP

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

מוצק

בעת תכנון מערכת, כדאי לקחת בחשבון את עקרונות SOLID הידועים:

S (אחריות יחידה), O (פתוח-סגור), L (החלפת ליסקוב), I (הפרדת ממשק), D (היפוך תלות).

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

מִמְשָׁק

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

מעמד

כללי קידוד: מיצירת מערכת ועד לעבודה עם אובייקטים - 3

מקור תמונה

בואו נסתכל על איך השיעורים מסודרים באופן פנימי. או ליתר דיוק, כמה נקודות מבט וכללים שכדאי להקפיד עליהם בעת כתיבת שיעורים. ככלל, מחלקה צריכה להתחיל עם רשימה של משתנים בסדר מסוים:
  1. קבועים סטטיים ציבוריים;
  2. קבועים סטטיים פרטיים;
  3. משתני מופע פרטי.
אחר כך מגיעים הבנאים השונים, לפי הסדר מאלו עם הכי מעט ויכוחים לאלו שיש להם הכי הרבה. אחריהם עוקבות שיטות מהפומביות ביותר ועד הפרטיות ביותר. באופן כללי, שיטות פרטיות שמסתירות את היישום של פונקציונליות כלשהי שאנו רוצים להגביל נמצאות בתחתית.

גודל כיתה

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

חפצים

כימוס

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

חוק דמטר

אנו יכולים גם לשקול את חוק דמטר: זהו סט קטן של כללים המסייע בניהול מורכבות ברמת המחלקה והשיטה. נניח שיש לנו אובייקט Car , ויש לו שיטת move(Object arg1, Object arg2) . על פי חוק דמטר, שיטה זו מוגבלת לקריאה:
  • שיטות של אובייקט המכונית עצמו (במילים אחרות, האובייקט "זה");
  • שיטות של אובייקטים שנוצרו בשיטת העברה ;
  • שיטות של אובייקטים שהועברו כארגומנטים ( arg1 , arg2 );
  • שיטות של אובייקטים פנימיים של מכונית (שוב, "זה").
במילים אחרות, חוק דמטר הוא משהו כמו מה שהורים עשויים לומר לילד: "אתה יכול לדבר עם החברים שלך, אבל לא עם זרים".

מבנה נתונים

מבנה נתונים הוא אוסף של אלמנטים קשורים. כאשר בוחנים אובייקט כמבנה נתונים, יש קבוצה של רכיבי נתונים שהשיטות פועלות עליהם. קיומן של שיטות אלה מונחה באופן מרומז. כלומר, מבנה נתונים הוא אובייקט שמטרתו לאחסן ולעבוד עם (לעבד) את הנתונים המאוחסנים. ההבדל העיקרי שלו מאובייקט רגיל הוא שאובייקט רגיל הוא אוסף של שיטות הפועלות על רכיבי נתונים אשר מניחים באופן מרומז שהם קיימים. האם אתה מבין? ההיבט העיקרי של אובייקט רגיל הוא שיטות. משתנים פנימיים מקלים על פעולתם הנכונה. אבל במבנה נתונים, השיטות נמצאות שם כדי לתמוך בעבודה שלך עם רכיבי הנתונים המאוחסנים, שהם בעלי חשיבות עליונה כאן. סוג אחד של מבנה נתונים הוא אובייקט העברת נתונים (DTO). זוהי מחלקה עם משתנים ציבוריים וללא שיטות (או רק שיטות לקריאה/כתיבה) המשמשת להעברת נתונים בעבודה עם מסדי נתונים, ניתוח הודעות משקעים וכו'. בדרך כלל נתונים לא נשמרים באובייקטים כאלה לתקופה ממושכת. הוא מומר כמעט מיד לסוג הישות שהאפליקציה שלנו עובדת. ישות, בתורה, היא גם מבנה נתונים, אך מטרתה להשתתף בלוגיקה עסקית ברמות שונות של האפליקציה. מטרת DTO היא להעביר נתונים אל/מ האפליקציה. דוגמה ל-DTO:
@Setter
@Getter
@NoArgsConstructor
public class UserDto {
    private long id;
    private String firstName;
    private String lastName;
    private String email;
    private String password;
}
הכל נראה ברור מספיק, אבל כאן אנו לומדים על קיומם של כלאיים. היברידיות הן אובייקטים שיש להם שיטות לטיפול בהיגיון חשוב, לאחסן אלמנטים פנימיים, וכוללים גם שיטות עזר (קבל/סט). חפצים כאלה מבולגנים ומקשים על הוספת שיטות חדשות. כדאי להימנע מהם, כי לא ברור למה הם מיועדים - אחסון אלמנטים או ביצוע היגיון?

עקרונות יצירת משתנים

בואו נחשוב קצת על משתנים. ליתר דיוק, בואו נחשוב אילו עקרונות חלים בעת יצירתם:
  1. באופן אידיאלי, עליך להצהיר ולאתחל משתנה ממש לפני השימוש בו (אל תיצור משתנה ותשכח מזה).
  2. במידת האפשר, הכריז על משתנים כסופיים כדי למנוע את שינוי ערכם לאחר האתחול.
  3. אל תשכח את משתני המונה, שבהם אנו משתמשים בדרך כלל באיזשהו לולאת for . כלומר, אל תשכחו לאפס אותם. אחרת, כל ההיגיון שלנו עלול להישבר.
  4. כדאי לנסות לאתחל משתנים בבנאי.
  5. אם יש בחירה בין שימוש באובייקט עם הפניה או בלי ( new SomeObject() ), בחר בלי, שכן לאחר השימוש באובייקט הוא יימחק במהלך מחזור איסוף האשפה הבא והמשאבים שלו לא יתבזבזו.
  6. שמור על משך החיים של משתנה (המרחק בין יצירת המשתנה לפעם האחרונה בה מתייחסים אליו) קצר ככל האפשר.
  7. אתחול משתנים המשמשים בלולאה ממש לפני הלולאה, לא בתחילת השיטה המכילה את הלולאה.
  8. התחל תמיד עם ההיקף המצומצם ביותר והרחיב רק כשצריך (כדאי לנסות לעשות משתנה מקומי ככל האפשר).
  9. השתמש בכל משתנה למטרה אחת בלבד.
  10. הימנע ממשתנים עם מטרה נסתרת, למשל חלוקה של משתנה בין שתי משימות - זה אומר שהסוג שלו אינו מתאים לפתרון אחת מהן.

שיטות

כללי קידוד: מיצירת מערכת ועד לעבודה עם אובייקטים - 4

מתוך הסרט "מלחמת הכוכבים: פרק שלישי - נקמת הסית'" (2005)

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

  2. כלל מס' 2 — if , else , while והצהרות אחרות לא אמורות לכלול בלוקים מקוננים בכבדות: הרבה קינון מפחית משמעותית את הקריאות של הקוד. באופן אידיאלי, לא יהיו לך יותר משני בלוקים מקוננים של {} .

    ורצוי גם לשמור על הקוד בבלוקים אלו קומפקטי ופשוט.

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

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

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

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

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

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

  7. אם למתודה יש ​​מספר רב של פרמטרי קלט (קיצוני הוא 7, אבל באמת כדאי להתחיל לחשוב אחרי 2-3), יש לקבץ חלק מהארגומנטים לאובייקט נפרד.

  8. אם יש כמה שיטות דומות (עמוסות יתר), אז פרמטרים דומים חייבים לעבור באותו סדר: זה משפר את הקריאות והשימושיות.

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

  10. try/catch לא נראה מאוד נחמד בטבעו, אז זה יהיה רעיון טוב להעביר אותו לשיטת ביניים נפרדת (שיטה לטיפול בחריגים):

    public void exceptionHandling(SomeObject obj) {
        try {
            someMethod(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

דיברתי על קוד כפול למעלה, אבל הרשו לי לחזור שוב: אם יש לנו כמה שיטות עם קוד חוזר, אנחנו צריכים להעביר אותו לשיטה נפרדת. זה יהפוך גם את השיטה וגם את המחלקה לקומפקטיות יותר. אל תשכח את הכללים השולטים בשמות: פרטים על איך לתת שם נכון למחלקות, ממשקים, שיטות ומשתנים יידונו בחלק הבא של המאמר. אבל זה כל מה שיש לי בשבילך היום.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION