สไตล์โค้ด AOSP Java สำหรับผู้ร่วมให้ข้อมูล

สไตล์โค้ดในเพจนี้เป็นกฎที่เข้มงวดสำหรับการใส่โค้ด Java ให้กับ Android Open Source Project (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 ) ได้รับการติดในการจัดการข้อผิดพลาดการตรวจสอบระดับ มันปิดบังคุณสมบัติการจัดการความล้มเหลวของโค้ดของคุณ ซึ่งหมายความว่าหากมีคนเพิ่มข้อยกเว้นประเภทใหม่ในโค้ดที่คุณกำลังเรียกใช้ คอมไพเลอร์จะไม่ชี้ให้เห็นว่าคุณต้องจัดการกับข้อผิดพลาดแบบอื่น ในกรณีส่วนใหญ่ คุณไม่ควรจัดการกับข้อยกเว้นประเภทต่างๆ ในลักษณะเดียวกัน

ข้อยกเว้นที่พบได้ยากสำหรับกฎนี้คือโค้ดทดสอบและโค้ดระดับบนสุด ซึ่งคุณต้องการตรวจจับข้อผิดพลาดทุกประเภท (เพื่อป้องกันไม่ให้แสดงใน UI หรือเพื่อให้งานแบทช์ทำงานต่อไป) ในกรณีนี้คุณอาจจับทั่วไป Exception (หรือ Throwable ) และจัดการกับข้อผิดพลาดได้อย่างเหมาะสม คิดให้รอบคอบก่อนที่จะทำสิ่งนี้ และใส่ความคิดเห็นที่อธิบายว่าเหตุใดจึงปลอดภัยในบริบทนี้

ทางเลือกในการจับข้อยกเว้นทั่วไป:

  • จับแต่ละข้อยกเว้นแยกเป็นส่วนหนึ่งของหลายบล็อกจับตัวอย่างเช่น:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • ปรับโครงสร้างโค้ดของคุณใหม่เพื่อให้มีการจัดการข้อผิดพลาดที่ละเอียดยิ่งขึ้นด้วยบล็อกการลองหลายอัน แยก IO ออกจากการแยกวิเคราะห์ และจัดการข้อผิดพลาดแยกกันในแต่ละกรณี
  • โยนข้อยกเว้นอีกครั้ง หลายครั้งคุณไม่จำเป็นต้องจับข้อยกเว้นในระดับนี้อยู่แล้ว เพียงแค่ปล่อยให้วิธีการโยนมัน

จำไว้ว่าข้อยกเว้นคือเพื่อนของคุณ! เมื่อคอมไพเลอร์บ่นว่าคุณไม่ได้รับข้อยกเว้นอย่าทำหน้าบึ้ง รอยยิ้ม! คอมไพเลอร์ช่วยให้คุณตรวจจับปัญหารันไทม์ในโค้ดได้ง่ายขึ้น

อย่าใช้ตัวสุดท้าย

Finalizers เป็นวิธีการหนึ่งในการรันโค้ดบางส่วนเมื่อวัตถุถูกรวบรวมเป็นขยะ แม้ว่า Finalizers จะมีประโยชน์สำหรับการล้างข้อมูล (โดยเฉพาะทรัพยากรภายนอก) ไม่มีการรับประกันว่าจะมีการเรียก Finalizer เมื่อใด (หรือแม้แต่จะถูกเรียกเลย)

Android ไม่ได้ใช้ตัวสุดท้าย ในกรณีส่วนใหญ่ คุณสามารถใช้การจัดการข้อยกเว้นที่ดีแทนได้ ถ้าคุณอย่างต้อง finalizer ที่กำหนด close() วิธีการ (หรือชอบ) และเอกสารว่าเมื่อวิธีการที่จะต้องมีการเรียกว่า (ดู InputStream สำหรับตัวอย่าง) ในกรณีนี้ เหมาะสมแต่ไม่จำเป็นต้องพิมพ์ข้อความบันทึกสั้นๆ จาก Finalizer ตราบใดที่ไม่คาดว่าจะท่วมบันทึก

นำเข้าอย่างครบถ้วน

เมื่อคุณต้องการใช้คลาส 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 2021 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 ของคุณจะบอกว่าคือ "ชุดฟู" หากวิธีการทำบางสิ่งที่ซับซ้อนมากขึ้น (เช่น การบังคับใช้ข้อจำกัดหรือมีผลข้างเคียงที่สำคัญ) คุณต้องจัดทำเป็นเอกสาร หากไม่ชัดเจนว่าคุณสมบัติ "Foo" หมายถึงอะไร คุณควรจัดทำเป็นเอกสาร

ทุกวิธีที่คุณเขียน ทั้งแบบสาธารณะและแบบอื่นๆ จะได้รับประโยชน์จาก Javadoc เมธอดสาธารณะเป็นส่วนหนึ่งของ API ดังนั้นจึงต้องใช้ Javadoc Android ไม่บังคับใช้ลักษณะที่เฉพาะเจาะจงสำหรับการเขียนความคิดเห็น Javadoc แต่คุณควรปฏิบัติตามคำแนะนำใน วิธีการเขียนความคิดเห็นหมอสำหรับเครื่องมือ Javadoc

เขียนวิธีสั้นๆ

เมื่อทำได้ ให้ใช้วิธีเล็กและเน้น เราทราบดีว่าบางครั้งวิธีการแบบยาวก็เหมาะสม ดังนั้นจึงไม่มีการจำกัดความยาวของวิธีแบบตายตัว หากวิธีการใดเกิน 40 บรรทัด ให้คิดดูว่าสามารถแบ่งได้โดยไม่ทำลายโครงสร้างของโปรแกรมหรือไม่

กำหนดฟิลด์ในตำแหน่งมาตรฐาน

กำหนดฟิลด์ที่ด้านบนสุดของไฟล์หรือก่อนเมธอดที่ใช้

จำกัดขอบเขตตัวแปร

รักษาขอบเขตของตัวแปรท้องถิ่นให้น้อยที่สุด ซึ่งจะช่วยเพิ่มความสามารถในการอ่านและบำรุงรักษาโค้ดของคุณ และลดโอกาสเกิดข้อผิดพลาด ประกาศตัวแปรแต่ละตัวในบล็อกชั้นในสุดที่ล้อมรอบการใช้งานตัวแปรทั้งหมด

ประกาศตัวแปรท้องถิ่น ณ จุดที่ใช้งานครั้งแรก เกือบทุกการประกาศตัวแปรโลคัลควรมีตัวเริ่มต้น หากคุณยังไม่มีข้อมูลเพียงพอที่จะเริ่มต้นตัวแปรอย่างสมเหตุสมผล ให้เลื่อนการประกาศออกไปจนกว่าคุณจะทำ

ข้อยกเว้นคือคำสั่ง try-catch หากตัวแปรเริ่มต้นด้วยค่าส่งคืนของเมธอดที่ส่งข้อยกเว้นที่ตรวจสอบ ตัวแปรนั้นต้องเริ่มต้นภายในบล็อกการลอง หากต้องใช้ค่านอกบล็อก 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 )

เดิมทีไม่มีข้อกำหนดด้านรูปแบบในการสั่งซื้อ หมายความว่า 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 และมันควรจะตั้งชื่อการดำเนินการอื่น นอกจากนี้จำไว้ว่า @Deprecated วิธีการยังคงควรจะทำงาน ถ้าคุณเห็นรหัสเดิมที่มี @deprecated แท็ก Javadoc เพิ่ม @Deprecated คำอธิบายประกอบ
  • ใช้ @Override คำอธิบายประกอบเมื่อใดก็ตามที่วิธีการแทนที่การประกาศหรือการดำเนินงานจาก superclass ตัวอย่างเช่นถ้าคุณใช้ @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 คำอธิบายประกอบจะต้อง refactor รหัสเพื่อแยกองค์ประกอบของซอฟแวร์ที่ใช้คำอธิบายประกอบ

ใช้คำย่อเป็นคำ

ใช้ตัวย่อและตัวย่อเป็นคำในการตั้งชื่อตัวแปร เมธอด และคลาสเพื่อทำให้ชื่ออ่านง่ายขึ้น:

ดี แย่
XmlHttpRequest XMLHTTPRequest
รับรหัสลูกค้า รับรหัสลูกค้า
คลาส Html คลาส HTML
URL สตริง URL สตริง
id ยาว ID ยาว

เนื่องจากทั้ง 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 : ใช้เมื่อบางสิ่งบางอย่างที่ร้ายแรงเกิดขึ้นนั่นคือสิ่งที่จะมีผลกระทบที่ผู้ใช้มองเห็นและจะไม่สามารถกู้คืนได้โดยไม่ต้องลบข้อมูลบางส่วนถอนการติดตั้งแอพพลิเคเช็ดพาร์ทิชันข้อมูลหรือ reflashing อุปกรณ์ทั้งหมด (หรือแย่ลง) ระดับนี้จะถูกบันทึกไว้เสมอ ปัญหาที่แสดงให้เห็นถึงการเข้าสู่ระบบบางอย่างที่ ERROR ระดับมีผู้สมัครที่ดีที่จะมีการรายงานไปยังเซิร์ฟเวอร์สถิติการรวบรวม
  • WARNING : ใช้เมื่อสิ่งที่ร้ายแรงและไม่คาดคิดเกิดขึ้นนั่นคือสิ่งที่จะมีผลกระทบที่ผู้ใช้มองเห็น แต่มีแนวโน้มที่จะสามารถกู้คืนได้โดยไม่สูญเสียข้อมูลโดยการดำเนินการดำเนินการอย่างชัดเจนบางตั้งแต่รอหรือรีสตาร์ทแอปทุกวิธีการใหม่ในการดาวน์โหลด แอปเวอร์ชันใหม่หรือรีบูตอุปกรณ์ ระดับนี้จะถูกบันทึกไว้เสมอ ปัญหาที่ปรับเข้าสู่ระบบที่ WARNING ระดับนอกจากนี้ยังอาจได้รับการพิจารณาสำหรับการรายงานไปยังเซิร์ฟเวอร์สถิติการรวบรวม
  • INFORMATIVE : ใช้เพื่อทราบว่าสิ่งที่น่าสนใจที่เกิดขึ้นคือว่าเมื่อสถานการณ์มีการตรวจพบว่ามีแนวโน้มที่จะมีผลกระทบอย่างกว้างขวาง แต่ไม่จำเป็นต้องมีข้อผิดพลาด เงื่อนไขดังกล่าวควรได้รับการบันทึกโดยโมดูลที่เชื่อว่าเชื่อถือได้มากที่สุดในโดเมนนั้น (เพื่อหลีกเลี่ยงการบันทึกซ้ำโดยส่วนประกอบที่ไม่ได้รับอนุญาต) ระดับนี้จะถูกบันทึกไว้เสมอ
  • DEBUG : ใช้เพื่อทราบต่อสิ่งที่เกิดขึ้นบนอุปกรณ์ที่อาจจะเกี่ยวข้องกับการตรวจสอบและแก้ปัญหาพฤติกรรมที่ไม่คาดคิด บันทึกเฉพาะสิ่งที่จำเป็นในการรวบรวมข้อมูลที่เพียงพอเกี่ยวกับสิ่งที่เกิดขึ้นกับส่วนประกอบของคุณ หากบันทึกการดีบักของคุณมีอำนาจเหนือบันทึก คุณควรใช้การบันทึกแบบละเอียด

    ระดับนี้ถูกบันทึกไว้แม้ในรุ่นสร้างและจะต้องล้อมรอบด้วย if (LOCAL_LOG) หรือ if LOCAL_LOGD) บล็อกที่ LOCAL_LOG[D] ถูกกำหนดไว้ในชั้นเรียนหรือย่อยของคุณเพื่อให้มีความเป็นไปได้ที่จะปิดการใช้งานการเข้าสู่ระบบดังกล่าวทั้งหมด . ดังนั้นจะต้องไม่มีตรรกะที่ใช้งานใน if (LOCAL_LOG) บล็อก ทั้งหมดสตริงอาคารล็อกยังมีความต้องการที่จะอยู่ภายใน if (LOCAL_LOG) บล็อก อย่า refactor โทรเข้าสู่ระบบออกไปเรียกวิธีถ้ามันจะทำให้เกิดการสร้างสตริงจะใช้สถานที่นอก 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 ระดับและใส่มันด้วยถ้าบล็อกที่อนุญาตให้มีการปิดการใช้งานที่รวบรวมเวลา
  • ระวังเรื่องความปลอดภัยรั่วไหลผ่านบันทึก หลีกเลี่ยงการบันทึกข้อมูลส่วนตัว โดยเฉพาะอย่างยิ่ง หลีกเลี่ยงการบันทึกข้อมูลเกี่ยวกับเนื้อหาที่ได้รับการคุ้มครอง นี่เป็นสิ่งสำคัญโดยเฉพาะอย่างยิ่งเมื่อเขียนโค้ดเฟรมเวิร์ก เนื่องจากไม่ใช่เรื่องง่ายที่จะทราบล่วงหน้าว่าอะไรจะเป็นและจะไม่เป็นข้อมูลส่วนตัวหรือเนื้อหาที่ได้รับการคุ้มครอง
  • ไม่เคยใช้ 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))
}