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

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

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

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

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

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

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

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

เมื่อคุณต้องการใช้ class Bar จาก package foo มีสองวิธีในการนำเข้า:

  • import foo.*;

    อาจลดจำนวนใบแจ้งยอดการนำเข้า

  • import foo.Bar;

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

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

กฎของไลบรารี Java

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

กฎสไตล์ Java

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

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

/*
 * Copyright 2022 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 เมธอดสาธารณะเป็นส่วนหนึ่งของ 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 เมื่อใดก็ตามที่เมธอดแทนที่การประกาศหรือการใช้งานจากซูเปอร์คลาส ตัวอย่างเช่น หากคุณใช้แท็ก @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 สตริง
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 ของคุณอยู่ในรูปแบบ "ในอนาคตจะทำอะไรบางอย่าง" ให้ตรวจสอบว่าคุณได้ใส่วันที่เฉพาะ ("แก้ไขภายในเดือนพฤศจิกายน 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 (ขึ้นอยู่กับว่าผลที่ตามมานั้นร้ายแรงพอและไม่คาดคิดมากพอที่จะบันทึกในบิลด์รุ่นหรือไม่)
  • การมีระบบไฟล์เต็มรูปแบบบนระบบไฟล์ที่เข้าถึงได้หรือในนามของแอปของบุคคลที่สามไม่ควรถูกบันทึกในระดับที่สูงกว่าข้อมูล
  • ข้อมูลที่ไม่ถูกต้องที่มาจากแหล่งที่ไม่น่าเชื่อถือใดๆ (รวมถึงไฟล์ใดๆ บนที่จัดเก็บข้อมูลที่ใช้ร่วมกัน หรือข้อมูลที่มาจากการเชื่อมต่อเครือข่าย) ถือเป็นข้อมูลที่คาดหวังและไม่ควรทริกเกอร์การบันทึกใดๆ ในระดับที่สูงกว่า 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))
}