نمط كود جافا AOSP للمساهمين

أنماط التعليمات البرمجية في هذه الصفحة هي قواعد صارمة للمساهمة بتعليمات Java البرمجية في مشروع Android مفتوح المصدر (AOSP). لا يتم قبول المساهمات في نظام Android الأساسي التي لا تلتزم بهذه القواعد بشكل عام. نحن ندرك أنه ليست كل التعليمات البرمجية الحالية تتبع هذه القواعد ، لكننا نتوقع أن تكون جميع التعليمات البرمجية الجديدة متوافقة. انظر الترميز فيما يتعلق بأمثلة للمصطلحات التي يجب استخدامها وتجنبها من أجل نظام بيئي أكثر شمولاً.

كن متسقا

كن متسقًا من أبسط القواعد. إذا كنت تقوم بتحرير كود ، فخذ بضع دقائق للنظر في الكود المحيط وتحديد نمطه. إذا كان هذا الرمز يستخدم مسافات حول عبارات if ، فيجب عليك أيضًا. إذا كانت تعليقات الكود تحتوي على مربعات صغيرة من النجوم حولها ، اجعل تعليقاتك تحتوي على مربعات صغيرة من النجوم حولها أيضًا.

الهدف من وجود إرشادات للأسلوب هو الحصول على مفردات مشتركة للترميز ، بحيث يمكن للقراء التركيز على ما تقوله ، بدلاً من التركيز على ما تقوله. نقدم هنا قواعد نمطية عالمية حتى تعرف المفردات ، ولكن الأسلوب المحلي مهم أيضًا. إذا بدت الشفرة التي تضيفها إلى ملف مختلفة اختلافًا جذريًا عن الكود الموجود حولها ، فإنها تُبعد القراء عن الإيقاع عند قراءتها. حاول تجنب ذلك.

قواعد لغة جافا

يتبع 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) {
        ...
    }
  • أعد بناء الكود الخاص بك للحصول على معالجة أكثر دقة للأخطاء ، مع كتل محاولة متعددة. قسّم الإدخال / الإخراج من التحليل ، وتعامل مع الأخطاء بشكل منفصل في كل حالة.
  • إعادة طرح الاستثناء. في كثير من الأحيان لا تحتاج إلى التقاط الاستثناء في هذا المستوى على أي حال ، فقط دع الطريقة تطرحه.

تذكر أن الاستثناءات هي صديقك! عندما يشتكي المترجم من أنك لا تلتقط استثناءً ، فلا تتعثر. يبتسم! قام المترجم بتسهيل اكتشاف مشاكل وقت التشغيل في التعليمات البرمجية الخاصة بك.

لا تستخدم الصيغ النهائية

تعد Finalizers طريقة لتنفيذ جزء من التعليمات البرمجية عندما يتم جمع كائن غير مهم. بينما يمكن أن يكون برنامج Finalizer في متناول اليد للتنظيف (خاصةً الموارد الخارجية) ، لا توجد ضمانات بشأن موعد استدعاء المصير النهائي (أو حتى أنه سيتم استدعاؤه على الإطلاق).

لا يستخدم Android Finalizers. في معظم الحالات ، يمكنك استخدام معالجة الاستثناءات الجيدة بدلاً من ذلك. إذا كنت في حاجة ماسة إلى أداة نهائية ، فحدد طريقة close() (أو ما شابه) ووثق بالضبط متى يجب استدعاء هذه الطريقة (انظر InputStream للحصول على مثال). في هذه الحالة ، من المناسب ، ولكن ليس مطلوبًا ، طباعة رسالة سجل قصيرة من أداة الإنهاء ، طالما أنه لا يُتوقع إغراق السجلات.

واردات مؤهلة بالكامل

عندما تريد استخدام class Bar من package foo ، فهناك طريقتان ممكنتان لاستيراده:

  • import foo.*;

    يحتمل أن يقلل من عدد بيانات الاستيراد.

  • import foo.Bar;

    يوضح الفئات المستخدمة ويكون الرمز أكثر قابلية للقراءة للمشرفين.

استخدم import foo.Bar; لاستيراد جميع أكواد Android. هناك استثناء صريح لمكتبات Java القياسية ( java.util.* ، java.io.* ، إلخ.) ورمز اختبار الوحدة ( junit.framework.* ).

قواعد مكتبة جافا

هناك اصطلاحات لاستخدام مكتبات وأدوات Java في Android. في بعض الحالات ، تم تغيير الاتفاقية بطرق مهمة وقد تستخدم التعليمات البرمجية القديمة نمطًا أو مكتبة مهملة. عند العمل باستخدام مثل هذا الرمز ، لا بأس في متابعة النمط الحالي. ومع ذلك ، عند إنشاء مكونات جديدة ، لا تستخدم أبدًا المكتبات المهملة.

قواعد أسلوب جافا

استخدم تعليقات جافادوك القياسية

يجب أن يحتوي كل ملف على بيان حقوق النشر في الجزء العلوي ، متبوعًا ببيانات الحزمة والاستيراد (كل كتلة مفصولة بسطر فارغ) ، وأخيرًا إعلان الفئة أو الواجهة. في تعليقات Javadoc ، صف ما تفعله الفئة أو الواجهة.

/*
 * Copyright 2023 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 {
    ...
}

يجب أن تحتوي كل فئة وطريقة عامة غير تافهة تكتبها على تعليق جافادوك مع جملة واحدة على الأقل تصف ما يفعله الفصل أو الطريقة. يجب أن تبدأ هذه الجملة بفعل وصفي بضمير الغائب.

أمثلة

/** 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 ، ولكن يجب عليك اتباع التعليمات الموجودة في كيفية كتابة تعليقات Doc لأداة 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. واردات Android
  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 حرف هي الحد الأقصى مع الاستثناءات التالية :

  • إذا احتوى سطر التعليق على أمر مثال أو عنوان URL حرفي أطول من 100 حرف ، فقد يكون هذا السطر أطول من 100 حرف لسهولة القص واللصق.
  • يمكن أن تتجاوز خطوط الاستيراد الحد الأقصى لأن البشر نادرًا ما يراهم (وهذا أيضًا يبسط كتابة الأداة).

استخدم شروح Java القياسية

يجب أن تسبق التعليقات التوضيحية المعدلات الأخرى لنفس عنصر اللغة. يمكن إدراج التعليقات التوضيحية البسيطة للعلامة (على سبيل المثال ، @Override ) في نفس السطر مع عنصر اللغة. إذا كان هناك العديد من التعليقات التوضيحية أو التعليقات التوضيحية ذات المعلمات ، فقم بإدراجها في قائمة واحدة في كل سطر بترتيب أبجدي.

ممارسات Android القياسية للتعليقات التوضيحية الثلاثة المحددة مسبقًا في Java هي:

  • استخدم التعليق التوضيحي @Deprecated عندما لا يُنصح باستخدام العنصر المشروح. إذا كنت تستخدم التعليق التوضيحي @Deprecated ، فيجب أن يكون لديك أيضًا علامة @deprecated Javadoc ويجب تسمية تطبيق بديل. بالإضافة إلى ذلك ، تذكر أنه لا يزال من المفترض أن تعمل طريقة @Deprecated . إذا رأيت رمزًا قديمًا يحتوي على علامة @deprecated Javadoc ، فأضف التعليق التوضيحي @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 مطلوبًا ، أعد تشكيل الكود لعزل عناصر البرنامج حيث ينطبق التعليق التوضيحي.

تعامل مع الاختصارات على أنها كلمات

تعامل مع الاختصارات والاختصارات ككلمات في تسمية المتغيرات والطرق والفئات لجعل الأسماء أكثر قابلية للقراءة:

جيد سيئ
طلب XmlHttp طلب XMLHTTP
getCustomerId getCustomerID
فئة 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 : استخدمه لتدوين ما يحدث على الجهاز بشكل أكبر والذي قد يكون ذا صلة بالتحقيق في السلوكيات غير المتوقعة وتصحيحها. سجل فقط ما هو مطلوب لجمع معلومات كافية حول ما يحدث مع المكون الخاص بك. إذا كانت سجلات تصحيح الأخطاء الخاصة بك تسيطر على السجل ، فيجب عليك استخدام التسجيل المطول.

    يتم تسجيل هذا المستوى حتى عند إنشاءات الإصدار ، ويجب أن يكون محاطًا 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 (اعتمادًا على ما إذا كانت العواقب خطيرة بدرجة كافية وغير متوقعة بما يكفي لتسجيل الدخول في إصدار الإصدار).
  • إن وجود نظام ملفات كامل على نظام ملفات يمكن الوصول إليه أو بالنيابة عن تطبيقات الطرف الثالث لا يجب أن يتم تسجيله على مستوى أعلى من INFORMATIVE.
  • تعتبر البيانات غير الصالحة الواردة من أي مصدر غير موثوق به (بما في ذلك أي ملف موجود على وحدة تخزين مشتركة ، أو بيانات واردة عبر اتصال شبكة) متوقعة ويجب ألا تؤدي إلى أي تسجيل على مستوى أعلى من 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 ، لذلك لا يكون لتعليمات الطباعة أي تأثيرات مرئية. ومع ذلك ، لا يزال يتم تنفيذ كل بناء السلسلة الذي يحدث لهذه المكالمات.
  • القاعدة الذهبية للتسجيل هي أن سجلاتك قد لا تدفع السجلات الأخرى دون داعٍ إلى الخروج من المخزن المؤقت ، تمامًا كما قد لا يدفع الآخرون سجلاتك.

قواعد أسلوب جافتيست

اتبع اصطلاحات تسمية طريقة الاختبار واستخدم شرطة سفلية لفصل ما يتم اختباره عن الحالة المحددة التي يتم اختبارها. هذا النمط يجعل من السهل معرفة الحالات التي يتم اختبارها. فمثلا:

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))
}