รูปแบบโค้ด AOSP Java สำหรับผู้มีส่วนร่วม

รูปแบบของโค้ดในหน้านี้ถือเป็นกฎที่เข้มงวดในการสนับสนุนโค้ด Java ให้กับ Android Open Source Project (AOSP) การมีส่วนร่วมในแพลตฟอร์ม Android ที่ไม่ปฏิบัติตามกฎเหล่านี้โดยทั่วไป จะไม่ได้รับการยอมรับ เราตระหนักดีว่าโค้ดที่มีอยู่นั้นไม่ได้เป็นไปตามกฎเหล่านี้ทั้งหมด แต่เราคาดหวังว่าโค้ดใหม่ทั้งหมดจะต้องเป็นไปตามข้อกำหนด ดู การเขียนโค้ดด้วยความเคารพ สำหรับตัวอย่างคำศัพท์ที่จะใช้และหลีกเลี่ยงสำหรับระบบนิเวศที่ครอบคลุมมากขึ้น

คงเส้นคงวา

กฎที่ง่ายที่สุดข้อหนึ่งคือต้องสอดคล้องกัน หากคุณกำลังแก้ไขโค้ด ให้ใช้เวลาสักครู่เพื่อดูโค้ดที่อยู่รอบๆ และกำหนดรูปแบบของโค้ด หากโค้ดนั้นใช้ช่องว่างรอบ if clause คุณก็ควรทำเช่นกัน หากความคิดเห็นของโค้ดมีกล่องดาวเล็กๆ อยู่รอบๆ ให้แสดงความคิดเห็นของคุณให้มีกล่องดาวเล็กๆ รอบๆ ด้วย

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

กฎของภาษาจาวา

Android ปฏิบัติตามแบบแผนการเขียนโค้ด Java มาตรฐานโดยมีกฎเพิ่มเติมที่อธิบายไว้ด้านล่าง

อย่าละเลยข้อยกเว้น

การเขียนโค้ดที่ละเว้นข้อยกเว้นอาจเป็นเรื่องที่น่าสนใจ เช่น:

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

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

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

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

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

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

อย่าใช้ตัวสรุป

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

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

นำเข้าคุณสมบัติครบถ้วน

เมื่อคุณต้องการใช้คลาส Bar จากแพ็คเกจ foo มีสองวิธีที่เป็นไปได้ในการนำเข้า:

  • import foo.*;

    อาจลดจำนวนคำสั่งการนำเข้า

  • import foo.Bar;

    ทำให้ชัดเจนว่าคลาสใดถูกใช้และโค้ดสามารถอ่านได้ง่ายขึ้นสำหรับผู้ดูแล

ใช้ import foo.Bar; สำหรับการนำเข้าโค้ด Android ทั้งหมด มีข้อยกเว้นที่ชัดเจนสำหรับไลบรารีมาตรฐาน Java ( java.util.* , java.io.* ฯลฯ) และโค้ดทดสอบหน่วย ( junit.framework.* )

กฎไลบรารี Java

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

กฎสไตล์ Java

ใช้ความคิดเห็นมาตรฐาน Javadoc

ทุกไฟล์ควรมีคำชี้แจงลิขสิทธิ์ที่ด้านบน ตามด้วยคำสั่งแพ็คเกจและการนำเข้า (แต่ละบล็อกคั่นด้วยบรรทัดว่าง) และสุดท้ายคือการประกาศคลาสหรืออินเทอร์เฟซ ในความคิดเห็น Javadoc ให้อธิบายว่าคลาสหรืออินเทอร์เฟซทำอะไร

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

ทุกวิธีที่คุณเขียน ไม่ว่าจะเป็นแบบสาธารณะหรืออย่างอื่น จะได้ประโยชน์จาก Javadoc วิธีการสาธารณะเป็นส่วนหนึ่งของ API และดังนั้นจึงต้องใช้ Javadoc Android ไม่ได้บังคับใช้รูปแบบเฉพาะสำหรับการเขียนความคิดเห็น Javadoc แต่คุณควรปฏิบัติตามคำแนะนำใน วิธีเขียนความคิดเห็น Doc สำหรับเครื่องมือ 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 เมื่อใดก็ตามที่เมธอดแทนที่การประกาศหรือการนำไปใช้งานจากซูเปอร์คลาส ตัวอย่างเช่น หากคุณใช้แท็ก @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
รับรหัสลูกค้า รับรหัสลูกค้า
คลาส Html คลาส HTML
URL สตริง URL สตริง
รหัสยาว รหัสยาว

เนื่องจากฐานโค้ด JDK และ Android มีความไม่สอดคล้องกันกับตัวย่อ จึงแทบจะเป็นไปไม่ได้เลยที่จะสอดคล้องกับโค้ดที่อยู่รอบ ๆ ดังนั้น ควรถือว่าคำย่อเป็นเหมือนคำเสมอ

ใช้ความคิดเห็นสิ่งที่ต้องทำ

ใช้ความคิดเห็นของ 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 ของคุณอยู่ในรูปแบบ "ในวันที่ในอนาคต ให้ทำอะไรสักอย่าง" ตรวจสอบให้แน่ใจว่าคุณได้ระบุวันที่ที่ระบุ ("แก้ไขภายในเดือนพฤศจิกายน 2548") หรือเหตุการณ์เฉพาะ ("ลบโค้ดนี้หลังจากที่ตัวผสมการผลิตทั้งหมดเข้าใจโปรโตคอล 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 (ขึ้นอยู่กับว่าผลที่ตามมานั้นร้ายแรงเพียงพอและไม่คาดคิดเพียงพอที่จะบันทึกในรุ่น Release)
  • การมีระบบไฟล์เต็มรูปแบบบนระบบไฟล์ที่สามารถเข้าถึงได้หรือในนามของแอปของบุคคลที่สามไม่ควรบันทึกในระดับที่สูงกว่าข้อมูล
  • ข้อมูลที่ไม่ถูกต้องที่มาจากแหล่งที่ไม่น่าเชื่อถือ (รวมถึงไฟล์ใดๆ บนพื้นที่เก็บข้อมูลที่ใช้ร่วมกัน หรือข้อมูลที่มาจากการเชื่อมต่อเครือข่าย) ถือเป็นสิ่งที่คาดหวัง และไม่ควรทริกเกอร์การบันทึกใดๆ ที่ระดับที่สูงกว่า DEBUG เมื่อตรวจพบว่าไม่ถูกต้อง (และแม้แต่การบันทึกนั้นด้วยซ้ำ ควรจำกัดให้มากที่สุด)
  • เมื่อใช้กับออบเจ็กต์ String ตัวดำเนินการ + จะสร้างอินสแตนซ์ StringBuilder โดยปริยายด้วยขนาดบัฟเฟอร์เริ่มต้น (16 อักขระ) และอาจเป็น String ต์สตริงชั่วคราวอื่น ๆ ดังนั้นการสร้างวัตถุ StringBuilder อย่างชัดเจนจึงไม่แพงกว่าการใช้ตัวดำเนินการ + เริ่มต้น (และอาจมีประสิทธิภาพมากกว่ามาก) โปรดทราบว่าโค้ดที่เรียก Log.v() จะถูกคอมไพล์และดำเนินการในรุ่น release รวมถึงการสร้างสตริง แม้ว่าบันทึกจะไม่ได้ถูกอ่านก็ตาม
  • การบันทึกใดๆ ที่บุคคลอื่นตั้งใจให้อ่านและพร้อมใช้งานในรุ่น Release ควรจะสั้นกระชับโดยไม่เป็นความลับ และควรเข้าใจได้ ซึ่งรวมถึงการบันทึกทั้งหมดจนถึงระดับ DEBUG
  • เมื่อเป็นไปได้ ให้บันทึกต่อในบรรทัดเดียว อนุญาตให้มีความยาวบรรทัดได้สูงสุด 80 หรือ 100 อักขระ หลีกเลี่ยงความยาวที่ยาวเกินประมาณ 130 หรือ 160 อักขระ (รวมความยาวของแท็ก) หากเป็นไปได้
  • หากรายงานการบันทึกสำเร็จ ห้ามใช้ในระดับที่สูงกว่า VERBOSE
  • หากคุณใช้การบันทึกชั่วคราวเพื่อวินิจฉัยปัญหาที่ทำซ้ำได้ยาก ให้คงระดับ DEBUG หรือ VERBOSE ไว้ และปิดล้อมด้วย if block ที่อนุญาตให้ปิดการใช้งานในขณะคอมไพล์
  • ระวังเรื่องความปลอดภัยรั่วไหลผ่านบันทึก หลีกเลี่ยงการบันทึกข้อมูลส่วนตัว โดยเฉพาะอย่างยิ่ง หลีกเลี่ยงข้อมูลการบันทึกเกี่ยวกับเนื้อหาที่ได้รับการคุ้มครอง นี่เป็นสิ่งสำคัญอย่างยิ่งเมื่อเขียนโค้ดกรอบงาน เนื่องจากไม่ใช่เรื่องง่ายที่จะทราบล่วงหน้าว่าอะไรจะเป็นและจะไม่เป็นข้อมูลส่วนตัวหรือเนื้อหาที่ได้รับการคุ้มครอง
  • ห้ามใช้ 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))
}