AOSP Java Code Style עבור תורמים

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

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

היה עקבי

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

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

חוקי שפת Java

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

אל תתעלם מהחריגים

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

  void setServerPort(String value) {
      try {
          serverPort = Integer.parseInt(value);
      } catch (NumberFormatException e) { }
  }

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

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

חלופות מקובלות (לפי סדר העדפה) הן:

  • זרוק את החריגה אל המתקשר של השיטה שלך.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • זרוק חריג חדש שמתאים לרמת ההפשטה שלך.
      void setServerPort(String value) throws ConfigurationException {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new ConfigurationException("Port " + value + " is not valid.");
        }
      }
    
  • טפל בשגיאה בחן והחלף ערך מתאים בבלוק ה- catch {} .
      /** Set port. If value is not a valid number, 80 is substituted. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            serverPort = 80;  // default port for server
        }
      }
    
  • תפוס את החריג וזרוק מופע חדש של RuntimeException . זה מסוכן, אז עשה זאת רק אם אתה בטוח שאם שגיאה זו מתרחשת, הדבר המתאים לעשות הוא לקרוס.
      /** Set port. If value is not a valid number, die. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new RuntimeException("port " + value " is invalid, ", e);
        }
      }
    
  • כמוצא אחרון, אם אתה בטוח שהתעלמות מהחריג היא ראויה, אתה יכול להתעלם ממנו, אבל אתה חייב גם להגיב למה עם סיבה טובה.
    /** If value is not a valid number, original port number is used. */
    
    void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            // Method is documented to just ignore invalid user input.
            // serverPort will just be unchanged.
        }
    }
    

אל תתפוס חריגים גנריים

זה יכול להיות מפתה להתעצל כאשר תופסים חריגים ולעשות משהו כזה:

  try {
      someComplicatedIOFunction();        // may throw IOException
      someComplicatedParsingFunction();   // may throw ParsingException
      someComplicatedSecurityFunction();  // may throw SecurityException
      // phew, made it all the way
  } catch (Exception e) {                 // I'll just catch all exceptions
      handleError();                      // with one generic handler!
  }

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

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

חלופות לתפיסת חריגים גנריים:

  • תפוס כל חריג בנפרד כחלק מבלוק מרובה-תפיסה, לדוגמה:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • שנה את הקוד שלך לטיפול בשגיאות דק יותר, עם מספר בלוקים של ניסיון. פצל את ה-IO מהניתוח, וטפל בשגיאות בנפרד בכל מקרה.
  • זרוק מחדש את החריג. הרבה פעמים אתה ממילא לא צריך לתפוס את החריג ברמה הזו, פשוט תן לשיטה לזרוק אותו.

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

אל תשתמש בגמר

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

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

הכשרה מלאה של יבוא

כאשר אתה רוצה להשתמש ב-class Bar מחבילה foo , יש שתי דרכים אפשריות לייבא אותו:

  • import foo.*;

    מפחית פוטנציאלית את מספר הצהרות הייבוא.

  • import foo.Bar;

    מבהיר באילו מחלקות נעשה שימוש, והקוד קריא יותר עבור המתחזקים.

השתמש import foo.Bar; לייבוא ​​כל קוד אנדרואיד. חריג מפורש נעשה עבור ספריות סטנדרטיות של Java ( java.util.* , java.io.* וכו') וקוד בדיקת יחידות ( junit.framework.* ).

חוקי ספריית Java

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

כללי סגנון Java

השתמש בהערות סטנדרטיות של Javadoc

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

/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.foo;

import android.os.Blah;
import android.view.Yada;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Does X and Y and provides an abstraction for Z.
 */

public class Foo {
    ...
}

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

דוגמאות

/** Returns the correctly rounded positive square root of a double value. */

static double sqrt(double a) {
    ...
}

אוֹ

/**
 * Constructs a new String by converting the specified array of
 * bytes using the platform's default character encoding.
 */
public String(byte[] bytes) {
    ...
}

אינך צריך לכתוב Javadoc עבור שיטות get and set טריוויאליות כגון setFoo() אם כל מה שה-Javadoc שלך היה אומר הוא "sets Foo". אם השיטה עושה משהו מורכב יותר (כגון אכיפת אילוץ או בעלת תופעת לוואי חשובה), אז עליך לתעד זאת. אם לא ברור מה פירוש הנכס "פו", כדאי לתעד אותו.

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

כתוב שיטות קצרות

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

הגדר שדות במקומות סטנדרטיים

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

הגבל היקף משתנה

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

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

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

// Instantiate class cl, which represents some sort of Set

Set s = null;
try {
    s = (Set) cl.newInstance();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable");
}

// Exercise the set
s.addAll(Arrays.asList(args));

עם זאת, אתה אפילו יכול להימנע ממקרה זה על ידי עטיפה של בלוק ה-Try-catch בשיטה:

Set createSet(Class cl) {
    // Instantiate class cl, which represents some sort of Set
    try {
        return (Set) cl.newInstance();
    } catch(IllegalAccessException e) {
        throw new IllegalArgumentException(cl + " not accessible");
    } catch(InstantiationException e) {
        throw new IllegalArgumentException(cl + " not instantiable");
    }
}

...

// Exercise the set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

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

for (int i = 0; i < n; i++) {
    doSomething(i);
}

ו

for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomethingElse(i.next());
}

הזמנת הצהרות ייבוא

סדר הצהרות הייבוא ​​הוא:

  1. ייבוא ​​אנדרואיד
  2. יבוא מצדדים שלישיים ( com , junit , net , org )
  3. java ו- javax

כדי להתאים בדיוק להגדרות ה-IDE, הייבוא ​​צריך להיות:

  • אלפביתי בתוך כל קיבוץ, עם אותיות גדולות לפני אותיות קטנות (לדוגמה, Z לפני a)
  • מופרד בשורה ריקה בין כל קיבוץ מרכזי ( android , com , junit , net , org , java , javax )

במקור, לא הייתה דרישת סגנון בהזמנה, כלומר IDEs תמיד שינו את ההזמנה או מפתחי IDE היו צריכים להשבית את תכונות ניהול הייבוא ​​האוטומטי ולתחזק את הייבוא ​​באופן ידני. זה נחשב רע. כשנשאלו בסגנון ג'אווה, הסגנונות המועדפים היו מגוונים מאוד והסתכם בכך ש-Android צריך פשוט "לבחור סדר ולהיות עקבי". אז בחרנו סגנון, עדכנו את מדריך הסגנון, וגרמנו ל-IDEs לציית לו. אנו מצפים שכאשר משתמשי IDE עובדים על הקוד, הייבוא ​​בכל החבילות יתאים לדפוס זה ללא מאמץ הנדסי נוסף.

בחרנו בסגנון זה כך ש:

  • הייבוא ​​שאנשים רוצים להסתכל עליו קודם נוטים להיות בראש ( android ).
  • היבוא שאנשים רוצים להיראות לפחות נוטה להיות בתחתית ( java ).
  • בני אדם יכולים בקלות לעקוב אחר הסגנון.
  • IDEs יכולים לעקוב אחר הסגנון.

שימו יבוא סטטי מעל כל שאר היבוא שהוזמן באותו אופן כמו יבוא רגיל.

השתמש ברווחים להזחה

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

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

מוּמלָץ

Instrument i =
        someLongExpression(that, wouldNotFit, on, one, line);

לא מומלץ

Instrument i =
    someLongExpression(that, wouldNotFit, on, one, line);

עקוב אחר מוסכמות שמות השדות

  • שמות שדות לא ציבוריים ולא סטטיים מתחילים ב- m .
  • שמות שדות סטטיים מתחילים ב- s .
  • שדות אחרים מתחילים באות קטנה.
  • שדות סופיים סטטיים (קבועים, בלתי ניתנים לשינוי) הם ALL_CAPS_WITH_UNDERSCORES .

לדוגמה:

public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

השתמש בסגנון סד סטנדרטי

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

class MyClass {
    int func() {
        if (something) {
            // ...
        } else if (somethingElse) {
            // ...
        } else {
            // ...
        }
    }
}

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

if (condition) {
    body();
}

וזה מקובל:

if (condition) body();

אבל זה לא מקובל:

if (condition)
    body();  // bad!

הגבל את אורך הקו

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

  • אם שורת הערה מכילה פקודה לדוגמה או כתובת אתר מילולית ארוכה מ-100 תווים, שורה זו עשויה להיות ארוכה מ-100 תווים כדי להקל על הגזירה וההדבקה.
  • קווי יבוא יכולים לעבור את הגבול מכיוון שבני אדם כמעט ולא רואים אותם (זה גם מפשט את כתיבת הכלים).

השתמש בהערות Java סטנדרטיות

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

שיטות העבודה הסטנדרטיות של Android עבור שלושת ההערות המוגדרות מראש ב-Java הן:

  • השתמש @Deprecated בכל פעם שהשימוש ברכיב המובא אינו מעודד. אם אתה משתמש @Deprecated , חייב להיות לך גם תג @deprecated Javadoc והוא צריך לתת שם למימוש חלופי. בנוסף, זכור ששיטת @Deprecated עדיין אמורה לעבוד . אם אתה רואה קוד ישן שיש לו תג @deprecated ‎‏, הוסף את ההערה @Deprecated
  • השתמש @Override בכל פעם ששיטה עוקפת את ההצהרה או היישום ממחלקת-על. לדוגמה, אם אתה משתמש בתג @inheritdocs Javadoc, ונגזר ממחלקה (לא ממשק), עליך גם לציין שהמתודה עוקפת את השיטה של ​​מחלקת האב.
  • השתמש @SuppressWarnings רק בנסיבות שבהן אי אפשר לבטל אזהרה. אם אזהרה עוברת את מבחן ה"בלתי אפשרי לבטל" זה, יש להשתמש בביאור @SuppressWarnings , כדי להבטיח שכל האזהרות משקפות בעיות בפועל בקוד.

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

    // TODO: The third-party class com.third.useful.Utility.rotate() needs generics
    @SuppressWarnings("generic-cast")
    List<String> blix = Utility.rotate(blax);
    

    כאשר נדרשת הערת @SuppressWarnings , שחזר את הקוד כדי לבודד את רכיבי התוכנה שבהם ההערה חלה.

התייחסו לראשי תיבות כאל מילים

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

טוֹב רַע
XmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
מחלקה HTML מחלקה HTML
כתובת אתר של מחרוזת כתובת URL של מחרוזת
תעודה מזהה ארוכה תעודה מזהה ארוכה

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

השתמש בהערות TODO

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

// TODO: Remove this code after the UrlTable2 has been checked in.

ו

// TODO: Change this to use a flag instead of a constant.

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

התחבר במשורה

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

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

    רמה זו נרשמת אפילו בבניית גרסה, ונדרשת להיות מוקפת if (LOCAL_LOG) או if LOCAL_LOGD) , כאשר LOCAL_LOG[D] מוגדר במחלקה או ברכיב המשנה שלך, כך שתהיה אפשרות להשבית את כל רישום הרישום הזה. . לכן, לא חייב להיות הגיון פעיל בבלוק if (LOCAL_LOG) . כל בניית המחרוזת עבור היומן צריכה להיות ממוקמת בתוך הבלוק if (LOCAL_LOG) . אל תשנה מחדש את קריאת הרישום לקריאת מתודה אם היא תגרום לבניית המחרוזת להתרחש מחוץ if (LOCAL_LOG) .

    יש איזה קוד שעדיין אומר if (localLOGV) . זה נחשב מקובל גם כן, אם כי השם אינו תקני.

  • VERBOSE : השתמש לכל השאר. רמה זו מחוברת רק בבניית ניפוי באגים וצריכה להיות מוקפת בבלוק if (LOCAL_LOGV) (או שווה ערך) כך שניתן יהיה להידור כברירת מחדל. כל בניין מחרוזת נמחק ממבני שחרור וצריך להופיע בתוך בלוק if (LOCAL_LOGV) .

הערות

  • בתוך מודול נתון, מלבד ברמת VERBOSE , יש לדווח על שגיאה רק פעם אחת אם אפשר. בתוך שרשרת בודדת של קריאות פונקציה בתוך מודול, רק הפונקציה הפנימית ביותר צריכה להחזיר את השגיאה, והמתקשרים באותו מודול צריכים להוסיף רישום מסוים רק אם זה עוזר באופן משמעותי לבודד את הבעיה.
  • בשרשרת של מודולים, מלבד ברמת VERBOSE , כאשר מודול ברמה נמוכה יותר מזהה נתונים לא חוקיים המגיעים ממודול ברמה גבוהה יותר, המודול ברמה נמוכה יותר צריך לרשום את המצב הזה רק ביומן DEBUG , ורק אם הרישום מספק מידע שאינו זמין בדרך אחרת למתקשר. באופן ספציפי, אין צורך לרשום מצבים שבהם נזרק חריג (החריג צריך להכיל את כל המידע הרלוונטי), או שבהם המידע היחיד שנרשם כלול בקוד שגיאה. זה חשוב במיוחד באינטראקציה בין המסגרת לאפליקציות, ותנאים הנגרמים על ידי אפליקציות צד שלישי המטופלות כהלכה על ידי המסגרת לא אמורים להפעיל רישום גבוה מרמת DEBUG . המצבים היחידים שאמורים להפעיל רישום ברמת INFORMATIVE ומעלה הם כאשר מודול או אפליקציה מזהים שגיאה ברמה משלהם או מגיעה מרמה נמוכה יותר.
  • כאשר מצב שבדרך כלל מצדיק רישום מסוים עשוי להתרחש פעמים רבות, זה יכול להיות רעיון טוב ליישם מנגנון מגביל קצב כלשהו כדי למנוע הצפת היומנים בעותקים רבים של אותו מידע (או דומה מאוד).
  • אובדנים של קישוריות רשת נחשבים נפוצים וצפויים לחלוטין, ואין לתעד אותם ללא תשלום. אובדן קישוריות רשת שיש לו השלכות בתוך אפליקציה צריך להירשם ברמת DEBUG או VERBOSE (תלוי אם ההשלכות רציניות מספיק ובלתי צפויות מספיק כדי להירשם ב-build גרסה).
  • קיום מערכת קבצים מלאה במערכת קבצים שנגישה לאפליקציות של צד שלישי או מטעמן לא אמורה להירשם ברמה גבוהה ממידע.
  • נתונים לא חוקיים המגיעים מכל מקור לא מהימן (כולל כל קובץ באחסון משותף, או נתונים המגיעים דרך חיבור רשת) נחשבים כצפויים ואינם אמורים להפעיל רישום ברמה גבוהה מ- DEBUG כאשר הוא מזוהה כלא חוקי (וגם אז רישום צריך להיות מוגבל ככל האפשר).
  • כאשר נעשה שימוש באובייקטי String , האופרטור + יוצר באופן מרומז מופע StringBuilder עם גודל המאגר המוגדר כברירת מחדל (16 תווים) ופוטנציאל אובייקטי String זמניים אחרים. אז יצירה מפורשת של אובייקטי StringBuilder אינה יקרה יותר מהסתמכות על אופרטור ברירת המחדל + (ויכולה להיות הרבה יותר יעילה). זכור שקוד שקורא Log.v() ומבוצע בבניית גרסה, כולל בניית המחרוזות, גם אם היומנים אינם נקראים.
  • כל רישום שנועד להיקרא על ידי אנשים אחרים ולהיות זמין בבניית גרסה צריכה להיות תמציתית מבלי להיות קריפטית, וצריכה להיות מובן. זה כולל את כל הרישום עד לרמת DEBUG .
  • במידת האפשר, המשך להתחבר בשורה אחת. אורך שורה של עד 80 או 100 תווים מקובל. הימנע מאורך של יותר מ-130 או 160 תווים בערך (כולל אורך התג) אם אפשר.
  • אם הרישום מדווח על הצלחות, לעולם אל תשתמש בו ברמות גבוהות מ- VERBOSE .
  • אם אתה משתמש ברישום זמני כדי לאבחן בעיה שקשה לשחזר, שמור אותה ברמת DEBUG או VERBOSE אותה עם בלוקים אם המאפשרים לבטל אותה בזמן ההידור.
  • היזהר מדליפות אבטחה דרך היומן. הימנע מרישום מידע פרטי. בפרט, הימנע מרישום מידע על תוכן מוגן. זה חשוב במיוחד בעת כתיבת קוד מסגרת מכיוון שלא קל לדעת מראש מה יהיה מידע פרטי או תוכן מוגן ומה לא.
  • לעולם אל תשתמש ב- System.out.println() (או printf() עבור קוד מקורי). System.out ו- System.err מופנים אל /dev/null , כך שלהצהרות ההדפסה שלך אין השפעות נראות לעין. עם זאת, כל בניית המחרוזת שמתרחשת עבור שיחות אלה עדיין מבוצעת.
  • כלל הזהב של רישום הוא שייתכן שהיומנים שלך לא ידחפו יומנים אחרים מחוץ למאגר, בדיוק כפי שאחרים עשויים שלא לדחוף החוצה את שלך.

כללי סגנון Javatests

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

testMethod_specificCase1 testMethod_specificCase2

void testIsDistinguishable_protanopia() {
    ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA)
    assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK))
    assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y))
}