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

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

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

שמות נכונים

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

מתוך סדרת הטלוויזיה "שרלוק" (2010-2017)

מתן שמות לממשקים

לממשקים יש בדרך כלל שמות שמתחילים באות גדולה ונכתבים ב-CamelCase. בעת כתיבת ממשק, זה היה נחשב כתרגול טוב להוסיף את הקידומת "I" כדי לייעד אותו כממשק (לדוגמה, IUserService), אבל זה נראה די מכוער ומסיח את הדעת. במקרים כאלה, עדיף להשמיט את הקידומת (UserService) ולהוסיף "Impl" כסיומת לשם היישום שלו (למשל UserServiceImpl). או אולי, כמוצא אחרון, הוסף קידומת "C" לשם היישום (למשל CUserService).

שמות כיתות

בדיוק כמו ממשקים, שמות מחלקות באותיות רישיות ומשתמשים ב-CamelCase. זה לא משנה אם אנו עומדים בפני אפוקליפסת זומבים, זה לא משנה אם הסוף בהישג יד - לעולם, לעולם, לעולם לא צריך השם של כיתה להיות פועל! שמות מחלקות ואובייקטים חייבים להיות שמות עצם או שמות עצם מורכבים (UserController, UserDetails, User Account וכן הלאה). אתה לא צריך להדביק את הקיצור של היישום על סוף השם של כל מחלקה, מכיוון שזה רק יוסיף מורכבות מיותרת. לדוגמה, אם יש לנו יישום User Data Migration, אז נא לא להוסיף "UDM" לכל מחלקה, כלומר UDMUserDetails, UDMUserAccount, UDMUserController.

שמות שיטות

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

שמות משתנים

ברוב המקרים, שמות משתנים מתחילים באות קטנה ומשתמשים גם ב-camelCase, למעט כאשר המשתנה הוא קבוע גלובלי. במקרים כאלה, כל אותיות השם נכתבות באותיות רישיות והמילים מופרדות בקו תחתון ("_"). מטעמי נוחות, אתה יכול להשתמש בהקשר משמעותי בעת מתן שמות למשתנים. במילים אחרות, כאשר משתנה קיים כחלק ממשהו גדול יותר, למשל, firstName, lastName או סטטוס. במקרים כאלה, ניתן להוסיף קידומת המציינת את האובייקט שאליו שייך משתנה זה. לדוגמה: userFirstName, userLastName, userStatus. כדאי גם להימנע משמות דומים למשתנים כאשר יש להם משמעויות שונות לחלוטין. הנה כמה אנטונימים שנתקלים בהם לעתים קרובות בשמות משתנים:
  • להתחיל/סיום
  • ראשון אחרון
  • נעול/לא נעול
  • מינימום מקסימום
  • הקודם הבא
  • ישן חדש
  • נפתח/נסגר
  • גלוי/בלתי נראה
  • מקור/יעד
  • מקור/יעד
  • למעלה למטה

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

כשיש לנו משתנים כמו x או n או משהו כזה, אנחנו לא רואים מיד את כוונת האדם שכתב את הקוד. לא ברור מה n עושה. להבין את זה מצריך התבוננות זהירה יותר (וזה אומר זמן, זמן, זמן). לדוגמה, נניח שיש לנו שדה שמייצג את המזהה של המשתמש האחראי. במקום שם משתנה כלשהו כמו x או פשוט id, נקרא למשתנה הזה "responsibleUserId", מה שמשפר באופן מיידי את הקריאות ואת תוכן המידע. עם זאת, לשמות קצרים כמו n יש מקום כמשתנים מקומיים בשיטות קטנות, כאשר גוש הקוד הכולל את המשתנה הזה הוא רק באורך של כמה שורות, ושם השיטה מתאר בצורה מושלמת את מה שקורה שם. כשהוא רואה משתנה כזה, מפתח מבין שיש לו חשיבות משנית ויש לו היקף מוגבל מאוד. כתוצאה מכך, ל-scope יש תלות מסוימת באורך שם משתנה: ככל שהשם ארוך יותר, המשתנה גלובלי יותר ולהיפך. כדוגמה, הנה שיטה למצוא את המשתמש האחרון שנשמר לפי תאריך:
public User findLastUser() {
   return findAllUsers().stream()
           .sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
           .findFirst()
           .orElseThrow(() -> new ResourceNotFoundException("No user exists"));
}
כאן אנו משתמשים במשתנים בעלי שם קצר x ו-y כדי למיין את הזרם, ואז אנו שוכחים מהם.

אורך אופטימלי

נמשיך עם הנושא של אורך השם. אורך השם האופטימלי הוא איפשהו בין n ל-maximumNumberOfUsersInTheCurrentGroup. במילים אחרות, שמות קצרים סובלים מחוסר משמעות, בעוד שמות ארוכים מדי מאריכים את התוכנית מבלי להוסיף קריאות, ואנחנו פשוט מתעצלים לכתוב אותם בכל פעם. מלבד המקרה שתואר לעיל עבור משתנים בעלי שם קצר כמו n, יש להקפיד על אורך של כ-8-16 תווים. זה לא כלל קפדני, רק קו מנחה.

הבדלים קטנים

אני לא יכול שלא להזכיר הבדלים עדינים בשמות. זהו גם תרגול רע, מכיוון שההבדלים הללו יכולים להיות פשוט מבלבלים או לדרוש השקעת זמן נוסף כדי להבחין בהם. לדוגמה, קשה לזהות את ההבדל בין InvalidDataAccessApiUsageException ל-InvalidDataAccessResourceUsageException. לעתים קרובות יכול להיווצר בלבול גם כאשר משתמשים באותיות קטנות L ו-O, מכיוון שניתן לטעות בהן בקלות כ-1 ו-0. בחלק מהגופנים ההבדל ברור יותר, בחלקם פחות.

המשמעות

עלינו להפוך שמות למשמעותיים, אך לא ליצור עמימות באמצעות מילים נרדפות, שכן, למשל, ל-UserData ול-UserInfo יש למעשה אותה משמעות. במקרה זה, נצטרך לחפור עמוק יותר לתוך הקוד כדי להבין איזה אובייקט מסוים אנחנו צריכים. הימנע ממילים שאינן מעבירות מידע מועיל. לדוגמה, ב-firstNameString, למה אנחנו צריכים את המילה String? האם זה באמת יכול להיות אובייקט Date? ברור שלא. אז, אנחנו פשוט משתמשים ב-firstName. אני רוצה להזכיר גם משתנים בוליאניים. כדוגמה, קח בוליאני בשם flagDeleted. למילה דגל אין משמעות. יותר הגיוני לקרוא לזה הוא נמחק.

מֵידָע מַטעֶה

ברצוני לומר גם כמה מילים על מוסכמות שמות לא נכונות. נניח שיש לנו משתנה בשם userActivityList, אבל במקום להיות List, האובייקט הזה הוא סוג מיכל אחר או אובייקט אחסון מותאם אישית. זה יכול לבלבל את המתכנת הממוצע: עדיף לקרוא לזה משהו כמו userActivityGroup או userActivities.

לחפש

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

הערות

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

סוגי הערות

  • הערות משפטיות - הערות בתחילת כל קובץ מקור מסיבות משפטיות, למשל:

    * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
    * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

  • הערות אינפורמטיביות - הערות המייצגות הסבר של הקוד (מתן מידע נוסף או הסבר על הכוונה של קטע נתון בקוד).

    לדוגמה:

    /*
    * Combines the user from the database with the one passed for updating
    * When a field in requestUser is empty, it is filled with old data from foundUser
    */
    private User mergeUser(User requestUser, User foundUser) {
           return new User(
           foundUser.getId(),
           requestUser.getFirstName() == null ? requestUser.getFirstName() : foundUser.getFirstName(),
           requestUser.getMiddleName() == null ? requestUser.getMiddleName() : foundUser.getMiddleName(),
           requestUser.getLastName() == null ? requestUser.getLastName() : foundUser.getLastName(),
           requestUser.getAge() == null ? requestUser.getAge() : foundUser.getAge()
           );
           }

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

  • הערות אזהרה - הערה שנועדה להזהיר מפתחים אחרים מפני ההשלכות הלא רצויות של פעולה (לדוגמה, להזהיר אותם מדוע בדיקה סומנה כ-@תעלם):

    // Takes too long to run
    // Don't run if you don't have a lot of time
    @Ignore
    @Test
    public void someIntegrationTest() {
           ……
           }

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

    דוגמה תהיה:

    // TODO: Add a check for the current user ID (when the security context is created)
    
    @Override
    public Resource downloadFile(File file) {
           return fileManager.download(file);
           }

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

  • הערות מחזקות - הערות המדגישות את החשיבות של נסיבות שבמבט ראשון עשויות להיראות חסרות משמעות.

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

    Stream.of(IOUtils.resourceToString("/fill-scripts/" + x, StandardCharsets.UTF_8)
           .trim()
           .split(";"))
           .forEach(jdbcTemplate::update);
    // The trim() call is very important. It removes possible spaces at the end of the script
    // so that when we read and split into separate requests, we don't end up with empty ones

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

    הנה דוגמה לשיטה נפוצה למדי לעדכון משתמש:

    /**
    * Updates the passed fields for a user based on its id.
         *
    * @param id id of the user to be updated
    * @param user user with populated fields for updating
    * @return updated user
    */
           User update(Long id, User user);

הערות גרועות

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

    שקול את הדוגמה הזו:

    public void configureSomeSystem() {
           try{
           String configPath = filesLocation.concat("/").concat(CONFIGURATION_FILE);
           FileInputStream stream = new FileInputStream(configPath);
           } catch (FileNotFoundException e) {
           // If there is no configuration file, the default configuration is loaded
          }
    }

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

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

    בוא נראה דוגמה:

    public class JdbcConnection{
    public class JdbcConnection{
       /**
        * The logger associated with the current class
        */
       private Logger log = Logger.getLogger(JdbcConnection.class.getName());
    
       /**
        * Creates and returns a connection using the input parameters
        */
       public static Connection buildConnection(String url, String login, String password, String driver) throws Exception {
           Class.forName(driver);
           connection = DriverManager.getConnection(url, login, password);
           log.info("Created connection with db");
           return connection;
       }

    מה הטעם בתגובות כאלה? כל מה שהם מסבירים כבר ברור לחלוטין.

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

    /**
    * Helper method. Closes the connection with the scanner if isNotUsing is true
    */
    private void scanClose(Scanner scan, boolean isNotUsing) throws Exception {
       if (!isNotUsing) {
           throw new Exception("The scanner is still in use");
       } scan.close();
    }

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

  • הערות חובה - הערות שנחשבות חובה (למשל הערות Javadoc), אבל למעשה לפעמים נערמות יתר על המידה והן לא אמינות ומיותרות (עליך לחשוב אם ההערות האלה באמת נחוצות).

  • דוגמא:

    /**
    * Create a user based on the parameters
    * @param firstName first name of the created user
    * @param middleName middle name of the created user
    * @param lastName last name of the created user
    * @param age age of the created user
    * @param address address of the created user
    * @return user that was created
    */
    User createNewUser(String firstName, String middleName, String lastName, String age, String address);

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

  • יומן הערות - הערות שמתווספות לפעמים לתחילת מודול בכל פעם שהוא נערך (משהו כמו יומן שינויים).

    /**
    * Records kept since January 9, 2020;
    **********************************************************************
    * 9 Jan 2020: Providing a database connection using JDBC Connection;
    * 15 Jan 2020: Adding DAO-level interfaces for working with the database;
    * 23 Jan 2020: Adding integration tests for the database;
    * 28 Jan 2020: Implementation of DAO-level interfaces;
    * 1 Feb 2020: Development of interfaces for services,
    * in accordance with the requirements specified in user stories;
    * 16 Feb 2020: Implementation of service interfaces
    * (implementation of business logic related to the work of the database);
    * 25 Feb 2020: Adding tests for services;
    * 8 Mar 2020: Celebration of International Women's Day (Terry is drunk again);
    * 21 Mar 2020: Refactoring the service layer;
    */

    גישה זו הייתה מוצדקת פעם, אך עם הופעתן של מערכות בקרת גרסאות (למשל, Git), היא הפכה לבלגן וסיבוך מיותר של הקוד.

  • הערות מחבר - הערות שמטרתן לציין את האדם שכתב את הקוד, כך שתוכל ליצור איתו/ה קשר ולדון כיצד, מה ולמה, למשל:

    * @author Bender Bending

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

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

    //    public void someMethod(SomeObject obj) {
    //    .....
    //    }

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

  • הערות לא ברורות - הערות שמתארות משהו בצורה מסובכת מדי.

    /*
        * Start with an array large enough to store
        * all the data bytes (plus filter bytes) with a cushion, plus 300 bytes
        * for header data
        */
    this.dataBytes = new byte[(this.size * (this.deep + 1) * 2)+300];

    הערה צריכה להסביר את הקוד. זה עצמו לא צריך הסבר. אז מה לא בסדר כאן? מהם "בתים סינון"? מה זה ה"+1" הזה? למה בדיוק 300?

אם כבר החלטת לכתוב תגובות, הנה כמה טיפים:
  1. השתמשו בסגנונות קלים לתחזוקה: שמירה על סגנונות מפוארים ואקזוטיים מדי היא מעצבנת וגוזלת זמן.
  2. אין להשתמש בהערות סוף שורה המתייחסות לשורות בודדות: התוצאה היא ערימה גדולה של הערות. מה גם שקשה לחשוב על הערה משמעותית לכל שורה.
  3. כשאתה כותב הערה, נסה לענות על השאלה "למה", לא "איך".
  4. הימנע ממידע מקוצר. כפי שאמרתי לעיל, איננו צריכים הסבר להערה: ההערה עצמה היא ההסבר.
  5. אתה יכול להשתמש בהערות כדי לרשום יחידות וטווחי ערכים.
  6. הצב הערות קרוב לקוד שהם מתארים.
לבסוף, אני עדיין רוצה להזכיר לך שההערה הטובה ביותר היא לא הערה, אלא השימוש בשמות מיומנים לאורך היישום שלך. ככלל, רוב הזמן נעבוד עם קוד קיים, תחזוק והארכתו. זה הרבה יותר נוח כאשר הקוד הזה קל לקריאה ומובן, שכן קוד רע הוא מכשול. זה כמו לזרוק מפתח ברגים לעבודה, וחיפזון הוא בן לוויה הנאמן שלו. וככל שיש לנו יותר קוד גרוע, הביצועים יורדים יותר. זה אומר שאנחנו צריכים לעשות מחדש מעת לעת. אבל אם מלכתחילה תנסו לכתוב קוד שלא יגרום למפתחים הבאים לרצות למצוא ולהרוג אתכם, אז לא תצטרכו לשחזר אותו באותה תדירות. אבל זה עדיין יהיה הכרחי, שכן התנאים והדרישות של המוצר משתנים כל הזמן עם תוספת של תלות וקשרים חדשים. ובכן, אני מניח שזה הכל בשבילי היום. תודה לכל מי שקרא עד כאן :)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION