บทความนี้แสดงรายการข้อผิดพลาดทั่วไปบางอย่างที่ฉันเห็นในโค้ด Java ของเพื่อนร่วมงานที่อยู่รอบตัวฉัน แน่นอนว่าการวิเคราะห์โค้ดแบบสแตติก (ทีมของเราใช้ qulice) จะไม่สามารถค้นหาปัญหาทั้งหมดได้ นั่นคือเหตุผลที่ฉันแสดงรายการไว้ที่นี่
หากคุณคิดว่ามีบางอย่างขาดหายไป โปรดแจ้งให้เราทราบ เรายินดีที่จะเพิ่มให้
ข้อผิดพลาดทั้งหมดที่แสดงด้านล่างนี้โดยพื้นฐานแล้วเกี่ยวข้องกับการเขียนโปรแกรมเชิงวัตถุ โดยเฉพาะ OOP ของ Java
ชื่อชั้นเรียน
อ่านบทความสั้น ๆ นี้ "วัตถุคืออะไร" คลาสควรเป็นเอนทิตีเชิงนามธรรมในชีวิตจริง ไม่ใช่ "ผู้ตรวจสอบ" "ผู้ควบคุม" และ "ผู้จัดการ" หากชื่อชั้นเรียนของคุณลงท้ายด้วย "er" นั่นถือเป็นการออกแบบที่ไม่ดี
แน่นอนว่าคลาสเครื่องมือก็มีการต่อต้านรูปแบบเช่นกัน เช่น StringUtils, FileUtils และ IOUtils ของ Apache ที่กล่าวมาทั้งหมดเป็นตัวอย่างของการออกแบบที่ไม่ดี อ่านเพิ่มเติม: ทางเลือกแทนคลาสเครื่องมือใน OOP
แน่นอน อย่าใช้คำนำหน้าหรือคำต่อท้ายเพื่อแยกคลาสออกจากอินเทอร์เฟซ ตัวอย่างเช่น ชื่อเหล่านี้ไม่ถูกต้อง: IRecord, IfaceEmployee หรือ RecordInterface โดยทั่วไป ชื่ออินเทอร์เฟซควรเป็นชื่อของเอนทิตีในชีวิตจริง และชื่อคลาสควรอธิบายรายละเอียดการใช้งาน หากไม่มีสิ่งใดพิเศษเกี่ยวกับการใช้งาน คุณสามารถเรียกมันว่า Default, Simple หรืออะไรที่คล้ายกัน ตัวอย่างเช่น:
คัดลอกรหัสรหัสดังต่อไปนี้:
คลาส SimpleUser ใช้งาน User {};
คลาส DefaultRecord ใช้บันทึก {};
คลาสต่อท้ายนำไปใช้ชื่อ {};
คลาสตรวจสอบความถูกต้องใช้เนื้อหา {};
ชื่อวิธีการ
เมธอดสามารถคืนค่าหรือเป็นโมฆะได้ หากวิธีการส่งคืนค่า ชื่อของมันควรจะอธิบายสิ่งที่ส่งคืน เช่น (อย่าใช้คำนำหน้ารับ):
คัดลอกรหัสรหัสดังต่อไปนี้:
บูลีน isValid (ชื่อสตริง);
เนื้อหาสตริง();
int ageOf (ไฟล์ไฟล์);
หากส่งคืนเป็นโมฆะ ชื่อควรอธิบายว่ามันทำอะไร ตัวอย่างเช่น:
คัดลอกรหัสรหัสดังต่อไปนี้:
บันทึกเป็นโมฆะ (ไฟล์ไฟล์);
กระบวนการเป็นโมฆะ(งานงาน);
เป็นโมฆะผนวก (ไฟล์ไฟล์, บรรทัดสตริง);
มีข้อยกเว้นเพียงข้อเดียวสำหรับกฎที่เพิ่งกล่าวถึง - วิธีทดสอบของ JUnit ไม่นับรวม เรื่องนี้จะมีการหารือด้านล่าง
ชื่อของวิธีการทดสอบ
ในกรณีทดสอบ JUnit ชื่อเมธอดควรเป็นคำสั่งภาษาอังกฤษโดยไม่มีช่องว่าง จะชัดเจนยิ่งขึ้นด้วยตัวอย่าง:
คัดลอกรหัสรหัสดังต่อไปนี้:
-
* HttpRequest สามารถส่งคืนเนื้อหาในรูปแบบ Unicode
* @throws Exception หากการทดสอบล้มเหลว
-
โมฆะสาธารณะ returnItsContentInUnicode() พ่นข้อยกเว้น {
-
ประโยคแรกใน JavaDoc ของคุณควรขึ้นต้นด้วยชื่อของคลาสที่คุณต้องการทดสอบ ตามด้วย can ดังนั้น ประโยคแรกของคุณควรเป็นแบบ "somebody can do something"
ชื่อวิธีการก็เหมือนกัน เพียงแต่ไม่มีธีม ถ้าฉันเพิ่มหัวเรื่องไว้ตรงกลางของชื่อเมธอด ฉันจะได้รับประโยคที่สมบูรณ์ ดังตัวอย่างด้านบน: "HttpRequest ส่งคืนเนื้อหาในรูปแบบยูนิโค้ด"
โปรดทราบว่าชื่อของวิธีทดสอบไม่ได้ขึ้นต้นด้วย can เฉพาะความคิดเห็นใน JavaDoc เท่านั้นที่จะขึ้นต้นด้วย can นอกจากนี้ ชื่อเมธอดไม่ควรขึ้นต้นด้วยกริยา
ในทางปฏิบัติ วิธีที่ดีที่สุดคือประกาศวิธีทดสอบที่จะส่งข้อยกเว้น
ชื่อตัวแปร
หลีกเลี่ยงการรวมชื่อตัวแปร เช่น timeOfDay, firstItem หรือ httpRequest สิ่งนี้เป็นจริงสำหรับตัวแปรคลาสและตัวแปรภายในวิธีการ ชื่อตัวแปรควรยาวพอที่จะหลีกเลี่ยงความคลุมเครือภายในขอบเขตที่มองเห็นได้ แต่อย่ายาวเกินไปหากเป็นไปได้ ชื่อควรเป็นคำนามในรูปเอกพจน์หรือพหูพจน์ หรือคำย่อที่เหมาะสม ตัวอย่างเช่น:
คัดลอกรหัสรหัสดังต่อไปนี้:
แสดงรายการชื่อ <String>;
เป็นโมฆะ sendThroughProxy (ไฟล์ไฟล์, โปรโตคอลโปรโตคอล);
เนื้อหาไฟล์ส่วนตัว
คำขอ HttpRequest สาธารณะ
บางครั้ง ถ้า Constructor บันทึกพารามิเตอร์อินพุตไปยังอ็อบเจ็กต์ที่เพิ่งเริ่มต้นใหม่ ชื่อของพารามิเตอร์และแอตทริบิวต์คลาสอาจขัดแย้งกัน ในกรณีนี้ คำแนะนำของฉันคือลบสระออกและใช้ตัวย่อ
ตัวอย่าง:
คัดลอกรหัสรหัสดังต่อไปนี้:
ข้อความในชั้นเรียนสาธารณะ {
ผู้รับสตริงส่วนตัว
ข้อความสาธารณะ (สตริง rcpt) {
this.recipient = rcpt;
-
-
หลายครั้ง คุณสามารถบอกได้ว่าควรตั้งชื่อตัวแปรอะไรโดยดูจากชื่อคลาสของมัน เพียงใช้รูปแบบตัวพิมพ์เล็กซึ่งมีความน่าเชื่อถือดังนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
ไฟล์ไฟล์;
ผู้ใช้บริการ;
สาขาสาขา;
อย่างไรก็ตาม คุณไม่ควรทำเช่นนี้กับประเภทดั้งเดิม เช่น เลขจำนวนเต็มหรือสตริงสตริง
หากมีตัวแปรหลายตัวที่มีลักษณะต่างกัน ให้พิจารณาใช้คำคุณศัพท์ ตัวอย่างเช่น:
คัดลอกรหัสรหัสดังต่อไปนี้:
การติดต่อสตริง (สตริงซ้าย, สตริงขวา);
ตัวสร้าง
โดยไม่คำนึงถึงข้อยกเว้น ควรมีตัวสร้างเพียงตัวเดียวที่ใช้เก็บข้อมูลลงในตัวแปรอ็อบเจ็กต์ ตัวสร้างอื่นๆ เรียกตัวสร้างนี้ด้วยพารามิเตอร์ที่แตกต่างกัน ตัวอย่างเช่น:
คัดลอกรหัสรหัสดังต่อไปนี้:
เซิร์ฟเวอร์คลาสสาธารณะ {
ที่อยู่สตริงส่วนตัว
เซิร์ฟเวอร์สาธารณะ (สตริง uri) {
this.address = uri;
-
เซิร์ฟเวอร์สาธารณะ (URI uri) {
นี้(uri.toString());
-
-
ตัวแปรครั้งเดียว
ควรหลีกเลี่ยงตัวแปรที่ใช้แล้วทิ้งในทุกกรณี สิ่งที่ฉันหมายถึงโดย "ครั้งเดียว" ในที่นี้คือตัวแปรที่ใช้เพียงครั้งเดียว ตัวอย่างเช่นอันนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
ชื่อสตริง = "data.txt";
ส่งคืนไฟล์ใหม่ (ชื่อ);
ตัวแปรข้างต้นจะใช้เพียงครั้งเดียว ดังนั้นโค้ดนี้จึงสามารถปรับโครงสร้างใหม่ได้ดังนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
ส่งคืนไฟล์ใหม่ ("data.txt");
บางครั้ง ในบางกรณีซึ่งเกิดขึ้นไม่บ่อยนัก—โดยหลักแล้วเพื่อการจัดรูปแบบที่ดูดีขึ้น—อาจใช้ตัวแปรแบบใช้แล้วทิ้งได้ อย่างไรก็ตามควรหลีกเลี่ยงสิ่งนี้ให้มากที่สุด
ผิดปกติ
ไม่จำเป็นต้องพูดว่า คุณไม่ควรกลืนข้อยกเว้นด้วยตัวเอง แต่ควรผ่านมันไปให้สูงที่สุดเท่าที่จะเป็นไปได้ วิธีการส่วนตัวควรโยนข้อยกเว้นที่เลือกไว้เสมอ
อย่าใช้ข้อยกเว้นสำหรับการควบคุมการไหล ตัวอย่างเช่น รหัสต่อไปนี้ไม่ถูกต้อง:
คัดลอกรหัสรหัสดังต่อไปนี้:
ขนาด int;
พยายาม {
size = this.fileSize();
} จับ (IOException เช่น) {
ขนาด = 0;
-
แล้วคุณควรทำอย่างไรหาก IOException แจ้งว่า "ดิสก์เต็ม" คุณจะยังคิดว่าขนาดไฟล์เป็น 0 และดำเนินการประมวลผลต่อไปหรือไม่
การเยื้อง
เกี่ยวกับการเยื้อง กฎหลักคือวงเล็บเปิดจะสิ้นสุดที่ท้ายบรรทัดหรือปิดอยู่ในบรรทัดเดียวกัน (สิ่งที่ตรงกันข้ามจะเป็นจริงสำหรับวงเล็บปิด) ตัวอย่างเช่น ข้อมูลต่อไปนี้ไม่ถูกต้องเนื่องจากวงเล็บเปิดแรกไม่ได้ปิดในบรรทัดเดียวกัน และมีอักขระอื่นอยู่หลังจากนั้น วงเล็บปีกกาที่สองก็เป็นปัญหาเช่นกัน เนื่องจากนำหน้าด้วยอักขระ แต่วงเล็บเปิดที่สอดคล้องกันไม่อยู่ในบรรทัดเดียวกัน:
คัดลอกรหัสรหัสดังต่อไปนี้:
ไฟล์สุดท้าย ไฟล์ = ไฟล์ใหม่ (ไดเร็กทอรี,
"ไฟล์.txt");
การเยื้องที่ถูกต้องควรเป็นดังนี้:
คัดลอกรหัสรหัสดังต่อไปนี้:
StringUtils.join(
อาร์เรย์.asList(
"บรรทัดแรก",
"บรรทัดที่สอง",
StringUtils.join(
Arrays.asList("a", "b")
-
-
"ตัวแยก"
-
กฎสำคัญประการที่สองเกี่ยวกับการเยื้องคือคุณควรพยายามเขียนอักขระในบรรทัดพร้อมกันให้ได้มากที่สุด - ขีดจำกัดบนคือ 80 อักขระ ตัวอย่างข้างต้นไม่เป็นไปตามประเด็นนี้ แต่สามารถย่อขนาดได้:
คัดลอกรหัสรหัสดังต่อไปนี้:
StringUtils.join(
อาร์เรย์.asList(
"บรรทัดแรก", "บรรทัดที่สอง",
StringUtils.join(Arrays.asList("a", "b"))
-
"ตัวแยก"
-
ค่าคงที่ซ้ำซ้อน
ค่าคงที่ของชั้นเรียนควรใช้เมื่อคุณต้องการแบ่งปันข้อมูลภายในวิธีการของชั้นเรียน ข้อมูลควรจะไม่ซ้ำกันในชั้นเรียนของคุณ อย่าใช้ค่าคงที่แทนสตริงหรือตัวอักษรตัวเลข นี่เป็นแนวทางปฏิบัติที่แย่มากและจะทำให้โค้ดของคุณเสีย ค่าคงที่ (เช่นเดียวกับวัตถุใดๆ ใน OOP) ควรมีความหมายของตัวเองในโลกแห่งความเป็นจริง มาดูกันว่าค่าคงที่เหล่านี้มีความหมายอย่างไรในชีวิตจริง:
คัดลอกรหัสรหัสดังต่อไปนี้:
เอกสารคลาส {
สตริงสุดท้ายแบบคงที่ส่วนตัว D_LETTER = "D"; // การปฏิบัติที่ไม่ดี
ส่วนขยายสตริงสุดท้ายแบบคงที่ส่วนตัว = ".doc"; // แนวปฏิบัติที่ดี
-
ข้อผิดพลาดทั่วไปอีกประการหนึ่งคือการใช้ค่าคงที่ในการทดสอบหน่วยเพื่อหลีกเลี่ยงสตริงที่ซ้ำซ้อนหรือตัวอักษรตัวเลขในวิธีทดสอบ อย่าทำเช่นนี้! วิธีทดสอบแต่ละวิธีควรมีค่าอินพุตเฉพาะของตัวเอง
ใช้ข้อความหรือค่าใหม่ในแต่ละวิธีทดสอบใหม่ พวกเขาเป็นอิสระจากกัน เหตุใดพวกเขาจึงยังแชร์ค่าคงที่อินพุตเดียวกัน
ทดสอบการเชื่อมต่อข้อมูล
นี่คือตัวอย่างของการเชื่อมโยงข้อมูลในวิธีทดสอบ:
คัดลอกรหัสรหัสดังต่อไปนี้:
ผู้ใช้ ผู้ใช้ = ผู้ใช้ใหม่ ("เจฟฟ์");
// อาจเป็นรหัสอื่นที่นี่
MatcherAssert.assertThat(user.name(), Matchers.equalTo("เจฟฟ์"));
ในบรรทัดสุดท้าย "Jeff" จะเชื่อมโยงกับสตริงลิเทอรัลเดียวกันในบรรทัดแรก หากผ่านไปไม่กี่เดือน มีคนต้องการเปลี่ยนค่าในบรรทัดที่สาม เขาจะต้องใช้เวลาค้นหาว่า "Jeff" ถูกใช้ที่ไหนในวิธีเดียวกัน
เพื่อหลีกเลี่ยงปัญหานี้ คุณควรแนะนำตัวแปร