סגנון קוד AOSP Java לתורמים

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

חשוב להיות עקביים

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

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

כללים לשפת Java

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

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

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

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

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

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

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

  • מקצים את החריג לקורא של השיטה שלכם.
      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 מהניתוח וטיפול בשגיאות בנפרד בכל מקרה.
  • מסמנים מחדש את החריגה. פעמים רבות אין צורך להבין ברמה הזאת בכל מקרה, פשוט תנו לשיטה לזרוק אותה.

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

לא להשתמש במוצרים סופיים

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

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

ייבוא כשיר

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

  • import foo.*;

    הדבר עלול לצמצם את מספר הצהרות הייבוא.

  • import foo.Bar;

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

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

כללים בספריית Java

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

כללי סגנון Java

שימוש בתגובות רגילות ב-Javadoc

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

/*
 * Copyright yyyy 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 שכוללת לפחות משפט אחד שמתאר את השיעור או ה-method. המשפט הזה צריך להתחיל באדם שלישי פועל תיאורי.

דוגמאות

/** 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 בשביל לקבל שיטות טריוויאליות ולהגדיר שיטות כמו setFoo() אם כל מה שכתוב ב-Javadoc הוא "sets Foo". אם המיקום השיטה מבצעת פעולה מורכבת יותר (כמו אכיפה של או שיש לו תופעת לוואי חשובה), עליך לתעד אותו. אם לא, ברור מה המאפיין "Foo" כלומר, צריך לתעד אותו.

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

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

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

הגדרת שדות במקומות רגילים

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

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

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

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

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

// 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));

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

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 (int i = 0; i < n; i++) {
    doSomething(i);
}

וגם

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

דוחות ייבוא הזמנות

הסדר של הצהרות הייבוא הוא:

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

כדי שתהיה התאמה מדויקת בין ההגדרות של סביבת הפיתוח המשולבת (IDE), הייבוא צריך להיות:

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

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

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

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

מציבים את הייבוא הסטטי מעל כל שאר הייבוא באותו סדר ייבוא רגיל.

שימוש ברווחים לכניסת פסקה

אנחנו משתמשים בארבע (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 תווים הוא המספר המקסימלי עם חריגים:

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

שימוש בהערות Java רגילות

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

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

  • שימוש בהערה @Deprecated בכל פעם שלא מומלץ להשתמש ברכיב עם ההערה. אם משתמשים ההערה @Deprecated, צריך גם @deprecated לתג Javadoc והוא צריך לתת שם להטמעה חלופית. In addition, חשוב לזכור ששיטת @Deprecated עדיין אמורה לפעול. אם רואים קוד ישן עם תג Javadoc @deprecated, צריך להוסיף את הקוד הערה אחת (@Deprecated).
  • אפשר להשתמש בהערה @Override בכל פעם שיטה מבטלת את ההצהרה או את ההטמעה כיתת-על. לדוגמה, אם משתמשים בתג Javadoc @inheritdocs, וב- שנגזרת ממחלקה (לא מממשק), צריך גם להוסיף הערות ה-method מחליפה את ה-method של המחלקה ההורה.
  • שימוש בהערה @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 בקשת XMLHTTP
getCustomerId getCustomerID
class Html HTML של הכיתה
כתובת ה-URL של המחרוזת כתובת ה-URL של המחרוזת
מזהה ארוך מזהה ארוך

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

שימוש בתגובות מסוג 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: משמש כדי להציג פרטים נוספים על מה שקורה בדף שעשוי להיות רלוונטי לחקירה ולניפוי באגים, בתרחישים לא צפויים והתנהגויות. מתעדים רק את מה שצריך כדי לאסוף מספיק נתונים לגבי מה שקורה ברכיב. אם יומנים שולטים ביומן, אז צריך להשתמש בטקסט מפורט רישום ביומן.

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

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

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

הערות

  • במודול נתון, פרט לרמת VERBOSE, אירעה שגיאה צריך לדווח על כך רק פעם אחת, אם אפשר. בתוך שרשרת אחת של קריאות לפונקציה בתוך המודול, רק הפונקציה הפנימית ביותר תחזיר את השגיאה, ומתקשרים באותו מודול אמורים להוסיף רק רישום ביומן, אם זה עוזר באופן משמעותי לבודד את הבעיה.
  • בשרשרת של מודולים, פרט לרמת VERBOSE, כאשר מודול ברמה נמוכה יותר מזהה נתונים לא חוקיים שמגיעים מרמה גבוהה יותר המודול ברמה הנמוכה יותר צריך לרשום את המצב הזה רק יומן DEBUG, ורק אם הרישום ביומן מספק מידע שאינו זמין למתקשר. וספציפית, אין צורך מצבים ביומן שבהם גורם חריג (החריג צריך מכילים את כל המידע הרלוונטי), או שבהם המידע היחיד רישום ביומן נכלל בקוד שגיאה. במיוחד חשוב באינטראקציה בין ה-framework לבין האפליקציות, ותנאים שנגרמו על ידי אפליקציות צד שלישי שהן תקינות המטפלת ב-framework לא יכולה להפעיל רישום ביומן ברמה גבוהה יותר רמת DEBUG. המצבים היחידים שיפעילו רישום ביומן רמה INFORMATIVE ומעלה היא כשמודול או אפליקציה מזהים שגיאה ברמה שלה או ברמה נמוכה יותר.
  • במקרים שבהם סביר להניח שתנאי שבדרך כלל מצדיק רישום ביומן יתרחשו פעמים רבות, כדאי להטמיע מנגנון להגבלת קצב של יצירת הבקשות כדי למנוע גלישת יומנים עותקים כפולים של אותו מידע (או מידע דומה מאוד).
  • אובדן החיבור לרשת הוא תופעה שגרתית הצפוי, ולא צריך להירשם ביומן ללא הצדקה. אובדן הרשת של קישוריות שיש לה השלכות בתוך האפליקציה, צריך להירשם DEBUG או VERBOSE (בהתאם לרמה שבה ההשלכות הן חמורות מספיק ולא צפויות כדי להיכלל בגרסה build).
  • שימוש במערכת קבצים מלאה במערכת קבצים שנגישה או מופעלת אסור לרשום ברמה של אפליקציות צד שלישי גבוהה מ-INFORMATIVE.
  • נתונים לא חוקיים שמגיעים מכל מקור לא מהימן (כולל כל קובץ ב- נפח אחסון משותף, או נתונים שמגיעים דרך רשת חיבור) נחשב כצפוי ולא אמור לגרום להפעלה של רישום ביומן ברמה גבוהה מ-DEBUG כאשר מזוהה לא חוקי (וגם אז הרישום ביומן צריך להיות מוגבל ככל האפשר).
  • בשימוש על אובייקטים מסוג String, האופרטור + במרומז יוצר מופע StringBuilder עם ברירת המחדל מאגר הנתונים הזמני (16 תווים) ויכול להיות גם String זמני אובייקטים. לכן יצירה מפורשת של אובייקטים ב-StringBuilder לא יקר מאשר שימוש באופרטור + שמוגדר כברירת מחדל (ויכול להיות הרבה ליעילות גבוהה יותר). חשוב לזכור שהקוד שמתקשר Log.v() עובר הידור ומופעל בגרסאות build של גרסאות, כולל בניית המחרוזות, גם אם לא קוראים את היומנים.
  • כל רישום ביומן שמיועד לקריאה על ידי אנשים אחרים שזמינות בגרסאות build של גרסה צריכה להיות צבעונית ולא מסתורית, והן צריכות להיות מובנות. זה כולל את כל הרישום ביומן עד לרמת 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))
}