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 ) ومعالجة الخطأ بشكل مناسب. فكر جيدًا قبل القيام بذلك، وقم بكتابة تعليقات توضح سبب كونها آمنة في هذا السياق.

بدائل لالتقاط الاستثناءات العامة:

  • احصل على كل استثناء بشكل منفصل كجزء من كتلة multi-catch، على سبيل المثال:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • قم بإعادة صياغة التعليمات البرمجية الخاصة بك للحصول على معالجة أكثر دقة للأخطاء، مع كتل محاولة متعددة. قم بفصل الإدخال/الإخراج عن التحليل، ومعالجة الأخطاء بشكل منفصل في كل حالة.
  • إعادة طرح الاستثناء. في كثير من الأحيان لا تحتاج إلى التقاط الاستثناء عند هذا المستوى على أي حال، فقط دع الطريقة تتخلص منه.

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

لا تستخدم أدوات الإنهاء

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

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

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

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

  • import foo.*;

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

  • import foo.Bar;

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

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

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

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

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

استخدم تعليقات 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 للحصول على طرق تافهة وضبطها مثل setFoo() إذا كان كل ما يقوله Javadoc هو "sets Foo". إذا كانت الطريقة تفعل شيئًا أكثر تعقيدًا (مثل فرض قيد أو كان لها تأثير جانبي مهم)، فيجب عليك توثيقها. إذا لم يكن معنى الخاصية "Foo" واضحًا، فيجب عليك توثيقه.

كل طريقة تكتبها، عامة أو غير ذلك، ستستفيد من Javadoc. تعد الطرق العامة جزءًا من واجهة برمجة التطبيقات وبالتالي تتطلب 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 نفسها ما لم يكن هناك سبب مقنع للقيام بخلاف ذلك:

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 )

في الأصل، لم تكن هناك متطلبات نمط عند الطلب، مما يعني أن بيئات التطوير المتكاملة كانت إما تغير الترتيب دائمًا أو كان على مطوري IDE تعطيل ميزات إدارة الاستيراد التلقائية والحفاظ على الواردات يدويًا. وقد اعتبر هذا سيئا. عندما سئل عن نمط Java، تنوعت الأنماط المفضلة بشكل كبير وكان الأمر يتعلق بنظام 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 ، فيجب أن يكون لديك أيضًا علامة Javadoc @deprecated ويجب أن تسمي تطبيقًا بديلاً. بالإضافة إلى ذلك، تذكر أنه من المفترض أن تعمل الطريقة @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 مطلوبًا، قم بإعادة بناء الكود لعزل عناصر البرنامج التي ينطبق عليها التعليق التوضيحي.

تعامل مع الاختصارات ككلمات

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

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