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

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

כללי סגנון ג'אווה

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

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

/*
 * Copyright 2024 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 היו צריכים להשבית את תכונות ניהול הייבוא ​​האוטומטי ולתחזק את הייבוא ​​באופן ידני. זה נחשב רע. כשנשאלו בסגנון ג'אווה, הסגנונות המועדפים היו מגוונים מאוד והסתכם באנדרואיד שצריך פשוט "לבחור סדר ולהיות עקבי". אז בחרנו סגנון, עדכנו את מדריך הסגנונות, וגרמנו ל-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 עדיין אמורה לעבוד . אם אתה רואה קוד ישן שיש לו תג Javadoc @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 : השתמש לכל השאר. רמה זו מחוברת רק ב-bug builds וצריכה להיות מוקפת בלוק if (LOCAL_LOGV) (או שווה ערך) כך שניתן יהיה להידור כברירת מחדל. כל בניין מחרוזת נמחק ממבני שחרור וצריך להופיע בתוך בלוק if (LOCAL_LOGV) .

הערות

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