- 1. ตั้งชื่อให้สื่อเจตนา
- 2. ให้ฟังก์ชันมีเป้าหมายชัดเจน
- 3. จัดคลาสและโมดูลตามเหตุผลที่เปลี่ยนแปลง
- 4. ลดความซับซ้อนของเงื่อนไข
- 5. ลดความซ้ำของกฎที่ควรเปลี่ยนพร้อมกัน
- 6. ใช้โครงสร้างที่เรียบง่ายและพอดีกับปัญหา
- 7. ใช้ชนิดข้อมูลและค่าคงที่ให้ตรงความหมาย
- 8. เขียนคอมเมนต์อธิบายเหตุผลและข้อจำกัด
- 9. จัดการข้อผิดพลาดและผลข้างเคียงให้ชัดเจน
- 10. ทดสอบพฤติกรรมและ Refactor ทีละส่วน
- ตัวอย่าง Java: ปรับโค้ดคำนวณส่วนลด
- Clean Code, SOLID และ Refactoring เกี่ยวข้องกันอย่างไร
- ข้อผิดพลาดที่พบบ่อยเมื่อเริ่มใช้ Clean Code
- เช็กลิสต์สำหรับ Code Review
- แบบฝึกหัดสำหรับนักศึกษา
- แหล่งอ้างอิงและอ่านเพิ่มเติม
Clean Code คือแนวทางเขียนโค้ดให้ผู้อ่านเข้าใจเจตนาได้ชัดเจน เห็นขอบเขตความรับผิดชอบ และปรับเปลี่ยนได้โดยตรวจสอบผลกระทบได้ เป้าหมายคือช่วยให้ทีมดูแลซอฟต์แวร์ต่อได้ตลอดอายุระบบ

#1. ตั้งชื่อให้สื่อเจตนา
ชื่อควรบอกว่าเก็บข้อมูลอะไร ทำงานอะไร และใช้หน่วยใดเมื่อหน่วยมีผลต่อความเข้าใจ เลือกคำศัพท์ให้ตรงกับโดเมนและใช้คำเดียวกันอย่างสม่ำเสมอ
// ต้องเดาว่าแต่ละค่าหมายถึงอะไร
int d = 7;
int t = 3000;
// บอกความหมายและหน่วย
int reservationDurationDays = 7;
int requestTimeoutMillis = 3000;
ชื่อเมธอดควรสะท้อนพฤติกรรม เช่น calculateDiscount() สำหรับคำนวณส่วนลด และ saveOrder() สำหรับบันทึกคำสั่งซื้อ หากเมธอดบันทึกข้อมูลจริง การตั้งชื่อเพียง getOrder() จะทำให้ผู้อ่านคาดการณ์ผลข้างเคียงผิด
ชื่อไม่จำเป็นต้องยาวเสมอไป ตัวแปร i ในลูปสั้นอาจชัดเจนพอ แต่ตัวแปรที่ใช้ข้ามหลายบล็อกควรมีบริบทมากขึ้น
#2. ให้ฟังก์ชันมีเป้าหมายชัดเจน
ฟังก์ชันควรอธิบายได้ว่าให้ผลลัพธ์หรือจัดการเรื่องใด หากต้องอธิบายด้วยรายการงานหลายประเภท อาจถึงเวลาแยกความรับผิดชอบ
ตัวอย่างเมธอด processOrder() ที่ตรวจสอบคำสั่งซื้อ คำนวณราคา บันทึกฐานข้อมูล สร้าง PDF และส่งอีเมลทั้งหมด อาจแยกงานเหล่านี้ออกเป็นส่วนที่มีชื่อชัดเจน แล้วให้เมธอดระดับบนประสานงาน
// ตัวอย่างโครงสร้าง: สมมติว่ามี collaborator เหล่านี้อยู่แล้ว
public void placeOrder(Order order) {
orderValidator.validate(order);
orderRepository.save(order);
confirmationService.send(order);
}
การประสานหลายขั้นตอนอาจเป็นหน้าที่เดียวในระดับ use case ได้ ประเด็นคือแต่ละระดับควรมีรายละเอียดที่สอดคล้องกัน และชื่อควรอธิบายสิ่งที่ทำ
ไม่มีกฎตายตัวว่าฟังก์ชันต้องไม่เกิน 5, 10 หรือ 20 บรรทัด พิจารณาความเข้าใจง่าย จำนวนแนวคิด และผลกระทบของการแก้ไขร่วมกัน การแยกจนต้องเปิดอ่านเมธอดย่อยจำนวนมากก็เพิ่มภาระให้ผู้อ่านได้
#3. จัดคลาสและโมดูลตามเหตุผลที่เปลี่ยนแปลง
Single Responsibility Principle (SRP) ช่วยให้แยกส่วนที่เปลี่ยนด้วยเหตุผลต่างกันออกจากกัน Robert C. Martin อธิบายแนวคิดนี้ผ่านแหล่งที่มาของข้อกำหนดหรือผู้ที่ทำให้ระบบต้องเปลี่ยน [1]
ตัวอย่างระบบใบแจ้งหนี้:
| ส่วนของระบบ | เรื่องที่รับผิดชอบ | ตัวอย่างเหตุผลที่เปลี่ยน |
|---|---|---|
InvoiceCalculator |
คำนวณยอดใบแจ้งหนี้ | เปลี่ยนกติกาการคำนวณ |
InvoiceRepository |
อ่านและบันทึกข้อมูล | เปลี่ยนโครงสร้างฐานข้อมูล |
InvoicePdfRenderer |
จัดรูปแบบเอกสาร | เปลี่ยนหน้าตาใบแจ้งหนี้ |
SRP ไม่ได้หมายความว่าคลาสต้องมีเพียงเมธอดเดียว คลาสที่มีหลายเมธอดซึ่งดูแลแนวคิดเดียวกันอาจมีความรับผิดชอบชัดเจนอยู่แล้ว
#4. ลดความซับซ้อนของเงื่อนไข
ใช้ guard clause เพื่อจัดการกรณีที่ไม่เข้าเงื่อนไขหรือข้อมูลผิดสัญญาให้ชัดเจนตั้งแต่ต้น และตั้งชื่อให้เงื่อนไขที่ซับซ้อนเมื่อช่วยอธิบายกฎธุรกิจได้
// สมมติว่า order ไม่เป็น null
public boolean canShip(Order order) {
if (!order.isPaid()) {
return false;
}
if (!order.isInStock()) {
return false;
}
return true;
}
สำหรับเงื่อนไขสั้น โค้ดนี้อาจอ่านง่ายกว่า:
public boolean canShip(Order order) {
return order.isPaid() && order.isInStock();
}
ทั้งสองรูปแบบใช้ได้ ให้เลือกรูปแบบที่ทีมอ่านและตรวจสอบได้ง่าย ส่วน guard clause มีประโยชน์มากขึ้นเมื่อแต่ละกรณีมีรายละเอียดต่างกัน ไม่จำเป็นต้องเปลี่ยนทุก if เป็น early return
#5. ลดความซ้ำของกฎที่ควรเปลี่ยนพร้อมกัน
DRY หรือ Don't Repeat Yourself ช่วยให้กฎเดียวกันไม่กระจายหลายตำแหน่ง เช่น เกณฑ์สิทธิ์ส่วนลดที่ใช้ร่วมกันจริง ควรมีแหล่งอ้างอิงชัดเจน
แต่โค้ดที่หน้าตาเหมือนกันอาจเป็นคนละกฎ เช่น ส่วนลดสมาชิกกับค่าชดเชยสินค้า ซึ่งวันนี้ใช้อัตรา 10% เหมือนกัน แต่มีเจ้าของนโยบายและแนวโน้มเปลี่ยนต่างกัน การบังคับให้ใช้ฟังก์ชันเดียวกันอาจทำให้การแก้ไขเรื่องหนึ่งกระทบอีกเรื่อง
ก่อนรวมโค้ด ให้ถามว่า “หากข้อกำหนดเปลี่ยน ทั้งสองจุดควรเปลี่ยนพร้อมกันหรือไม่” ถ้าคำตอบคือใช่ การรวมอาจเหมาะสม ถ้ายังไม่ชัด อาจเก็บแยกไว้ก่อน
#6. ใช้โครงสร้างที่เรียบง่ายและพอดีกับปัญหา
KISS หรือ Keep It Simple สนับสนุนให้เลือกวิธีที่ทีมเข้าใจและดูแลได้ ส่วน YAGNI หรือ You Aren't Gonna Need It เตือนให้ระวังการสร้างความสามารถสำหรับอนาคตที่ยังไม่มีความจำเป็น [2]
ตัวอย่าง หากมีการส่งออกไฟล์ CSV เพียงรูปแบบเดียว อาจเริ่มจากเมธอดหรือคลาสธรรมดา เมื่อมีรูปแบบอื่นที่ต้องรองรับจริงจึงพิจารณา interface หรือ Strategy Pattern
ความเรียบง่ายยังต้องรองรับข้อกำหนดด้านความถูกต้อง ความปลอดภัย และประสิทธิภาพที่มีอยู่แล้ว หากข้อกำหนดกำหนดให้รองรับหลายผู้ให้บริการในปัจจุบัน การออกแบบขอบเขตผ่าน interface ก็อาจเป็นทางเลือกที่เรียบง่ายสำหรับสถานการณ์นั้น
#7. ใช้ชนิดข้อมูลและค่าคงที่ให้ตรงความหมาย
หลีกเลี่ยงตัวเลขหรือข้อความที่ผู้อ่านต้องเดา เช่น 1000 หรือ "P" โดยไม่มีบริบท
private static final int MAX_LOGIN_ATTEMPTS = 5;
private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(3);
ใช้ enum สำหรับชุดสถานะที่มีขอบเขตชัดเจน ใช้ชนิดข้อมูลสำหรับวันที่ เวลา หรือจำนวนเงินให้เหมาะกับโดเมน และพิจารณา value object เมื่อข้อมูลนั้นมีข้อกำหนดหรือพฤติกรรมเฉพาะ
อย่ารวมค่าที่เท่ากันโดยบังเอิญเป็นค่าคงที่เดียวกัน เช่น จำนวนครั้งล็อกอินสูงสุดกับจำนวนสินค้าที่แสดง แม้ทั้งคู่มีค่า 5 ก็เป็นคนละแนวคิด
ค่าที่เปลี่ยนตามสภาพแวดล้อมควรอยู่ใน configuration ตามความเหมาะสม ส่วนข้อมูลลับควรใช้กลไกจัดการ secret ของระบบ
#8. เขียนคอมเมนต์อธิบายเหตุผลและข้อจำกัด
คอมเมนต์มีประโยชน์เมื่อบอกสิ่งที่อ่านจากโค้ดตรง ๆ ไม่ได้ เช่น เหตุผลทางธุรกิจ ข้อจำกัดของระบบภายนอก หรือเงื่อนไขสำคัญของอัลกอริทึม
// ต้องเก็บลำดับเดิม เพราะระบบปลายทางใช้ลำดับรายการในการจับคู่ผลลัพธ์
List<Request> requests = new ArrayList<>(incomingRequests);
คอมเมนต์ที่เพียงบรรยายคำสั่งซ้ำอาจไม่ช่วย:
// เพิ่มค่า count ขึ้นหนึ่ง
count++;
เอกสาร API, Javadoc, ข้อกำหนดด้านความปลอดภัย และคำอธิบายสูตรคำนวณยังมีความสำคัญ ควรอัปเดตพร้อมโค้ด และลบโค้ดเก่าที่คอมเมนต์ทิ้งไว้เมื่อไม่จำเป็น โดยใช้ version control สำหรับย้อนดูประวัติ
#9. จัดการข้อผิดพลาดและผลข้างเคียงให้ชัดเจน
ระบุสัญญาของเมธอดว่าอะไรเป็น input ที่ยอมรับได้ ผลลัพธ์หมายถึงอะไร และกรณีผิดพลาดถูกส่งต่ออย่างไร
หลีกเลี่ยงการกลืน exception แล้วคืนค่าที่ดูเหมือนสำเร็จ เช่น อ่านฐานข้อมูลไม่สำเร็จแต่คืนลิสต์ว่างจนผู้เรียกเข้าใจว่าไม่มีข้อมูล
// ตัวอย่างบางส่วน: ใช้ภายในชั้นที่อ่านไฟล์
try {
return Files.readString(path);
} catch (IOException exception) {
throw new UncheckedIOException("Cannot read configuration file", exception);
}
ตัวอย่างนี้เก็บสาเหตุเดิมไว้ ให้ชั้นที่เหมาะสมแปลงเป็นข้อความสำหรับผู้ใช้หรือผลตอบกลับ API ตามสัญญาของระบบ ต้องระวังข้อมูลส่วนบุคคลและข้อมูลลับใน log หรือ error response ด้วย
อีกประเด็นคือผลข้างเคียง: เมธอดที่คำนวณราคาไม่ควรส่งอีเมลหรือแก้ไขฐานข้อมูลอย่างซ่อนเร้น การแยกการคำนวณออกจาก I/O ช่วยให้ทดสอบและไล่ปัญหาได้ง่ายขึ้น
#10. ทดสอบพฤติกรรมและ Refactor ทีละส่วน
Refactoring คือการปรับโครงสร้างภายในเพื่อให้เข้าใจและแก้ไขง่ายขึ้น โดยรักษาพฤติกรรมที่สังเกตได้ตามขอบเขตของระบบ [3]
แนวทางทำงาน:
- ระบุพฤติกรรมปัจจุบันและสัญญาที่ต้องคงไว้
- ตรวจสอบว่ามีชุดทดสอบที่จับความผิดพลาดสำคัญได้ หากยังไม่มี ให้เพิ่มกรณีที่จำเป็นก่อน
- เปลี่ยนทีละเรื่อง เช่น ตั้งชื่อใหม่ แยกเมธอด หรือย้ายกฎไปยังตำแหน่งที่เหมาะสม
- รันทดสอบที่เกี่ยวข้องและตรวจ diff
- เมื่อเพิ่มฟีเจอร์หรือแก้บั๊ก ให้แยกเจตนาการเปลี่ยนพฤติกรรมออกจากการปรับโครงสร้างให้ตรวจสอบได้
การทดสอบควรตรวจผลลัพธ์สำคัญ กรณีขอบ และการจัดการข้อผิดพลาด ไม่ผูกกับ private method หรือรายละเอียดภายในโดยไม่จำเป็น คะแนน coverage เป็นข้อมูลช่วยตรวจช่องว่าง แต่ไม่ยืนยันว่าการทดสอบตรวจความถูกต้องได้ครบ
#ตัวอย่าง Java: ปรับโค้ดคำนวณส่วนลด
#ข้อกำหนดของตัวอย่าง
- รับยอดซื้อเป็นบาทด้วย
BigDecimal - ยอดซื้อห้ามเป็น
nullและห้ามติดลบ - สมาชิกที่ซื้ออย่างน้อย 1,000 บาท ได้ส่วนลด 10%
- กรณีอื่นได้ส่วนลด 0 บาท
- คืนค่า “จำนวนเงินส่วนลด” สองตำแหน่งทศนิยม โดยใช้
HALF_UP - นโยบายการปัดเศษเป็นข้อกำหนดสมมติของตัวอย่าง ต้องตกลงตามระบบจริง
#ก่อนปรับปรุง
คลาสนี้ทำงานตามข้อกำหนด แต่ชื่อและโครงสร้างยังทำให้ต้องอ่านรายละเอียดเพื่อเข้าใจ
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
public final class DiscountCalculator {
public BigDecimal calculateDiscount(BigDecimal total, boolean member) {
Objects.requireNonNull(total, "total must not be null");
if (total.signum() < 0) {
throw new IllegalArgumentException("total must not be negative");
}
BigDecimal d = BigDecimal.ZERO;
if (member) {
if (total.compareTo(new BigDecimal("1000.00")) >= 0) {
d = total.multiply(new BigDecimal("0.10"));
}
}
return d.setScale(2, RoundingMode.HALF_UP);
}
}
#หลังปรับปรุง
เก็บเมธอดสาธารณะและพฤติกรรมเดิมไว้ ตั้งชื่อกฎให้ชัด และแยกการตรวจสอบ input ออกจากการคำนวณ
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
public final class DiscountCalculator {
private static final BigDecimal MINIMUM_MEMBER_TOTAL =
new BigDecimal("1000.00");
private static final BigDecimal MEMBER_DISCOUNT_RATE =
new BigDecimal("0.10");
private static final BigDecimal NO_DISCOUNT = new BigDecimal("0.00");
public BigDecimal calculateDiscount(BigDecimal total, boolean member) {
validateTotal(total);
if (!member || total.compareTo(MINIMUM_MEMBER_TOTAL) < 0) {
return NO_DISCOUNT;
}
return total.multiply(MEMBER_DISCOUNT_RATE)
.setScale(2, RoundingMode.HALF_UP);
}
private void validateTotal(BigDecimal total) {
Objects.requireNonNull(total, "total must not be null");
if (total.signum() < 0) {
throw new IllegalArgumentException("total must not be negative");
}
}
}
ตัวอย่างทั้งสองเป็นคนละเวอร์ชันของ DiscountCalculator.java ให้เลือกใช้ทีละเวอร์ชัน ใช้ BigDecimal จากสตริงเพื่อแทนค่าทศนิยมตามที่เขียนโดยตรง และใช้ compareTo() สำหรับเปรียบเทียบค่าตัวเลข
พารามิเตอร์ boolean เดียวในตัวอย่างยังมีความหมายที่จัดการได้ หากมี flag หลายตัวหรือรูปแบบการคำนวณหลายประเภท จึงค่อยพิจารณาชนิดข้อมูลที่อธิบายบริบทได้ชัดกว่า
#กรณีที่ควรใช้ตรวจสอบก่อนและหลัง
| ยอดซื้อ | สมาชิก | ผลลัพธ์ส่วนลด |
|---|---|---|
0.00 |
ใช่ | 0.00 |
999.99 |
ใช่ | 0.00 |
1000.00 |
ใช่ | 100.00 |
1500.00 |
ใช่ | 150.00 |
1000.05 |
ใช่ | 100.01 |
1500.00 |
ไม่ใช่ | 0.00 |
-1.00 |
ใช่หรือไม่ใช่ | IllegalArgumentException |
null |
ใช่หรือไม่ใช่ | NullPointerException |
ตารางนี้เป็นเกณฑ์ทดสอบที่เสนอสำหรับตัวอย่าง บทความไม่ได้แนบผลการรันทดสอบ Java
ในระบบจริงอาจต้องเพิ่มกฎเรื่องสกุลเงิน จำนวนตำแหน่งทศนิยมที่รับได้ การปัดเศษรายบรรทัดเทียบกับทั้งเอกสาร และข้อจำกัดของยอดเงินตามธุรกิจ
#Clean Code, SOLID และ Refactoring เกี่ยวข้องกันอย่างไร
| แนวคิด | จุดเน้น | ตัวอย่าง |
|---|---|---|
| Clean Code | ความชัดเจนของโค้ดและความสะดวกในการดูแล | ตั้งชื่อสื่อเจตนา ลดเงื่อนไขซ้อน |
| SOLID | หลักพิจารณาการออกแบบโมดูลและความสัมพันธ์ | แยกกฎคำนวณออกจาก persistence ตามเหตุผลที่เปลี่ยน |
| Refactoring | กระบวนการปรับโครงสร้างโดยรักษาพฤติกรรม | Extract Function, Rename, Move Function |
| Coding Standard | ข้อตกลงร่วมของทีมที่ตรวจสอบได้ | รูปแบบชื่อ การจัดย่อหน้าและ import |
แนวคิดเหล่านี้ใช้ร่วมกันได้ แต่การทำตาม SOLID หรือจัดรูปแบบด้วย formatter เพียงอย่างเดียวยังไม่ครอบคลุมทุกมิติของโค้ดที่ดูแลได้ง่าย
#ข้อผิดพลาดที่พบบ่อยเมื่อเริ่มใช้ Clean Code
| การใช้แบบตายตัว | สิ่งที่ควรพิจารณา |
|---|---|
| บังคับให้ทุกฟังก์ชันสั้นเท่ากัน | หนึ่งแนวคิดที่ชัดเจนอาจยาวได้ตามบริบท |
| สร้าง interface ให้ทุกคลาส | เพิ่มเมื่อมีขอบเขตหรือความแปรผันที่ต้องจัดการจริง |
| รวมทุกส่วนที่หน้าตาคล้ายกัน | ตรวจว่าเป็นกฎเดียวกันและควรเปลี่ยนพร้อมกันหรือไม่ |
| ลบคอมเมนต์ทั้งหมด | เก็บเหตุผล ข้อจำกัด และสัญญาที่โค้ดสื่อไม่ได้ |
| ใช้ one-liner เพื่อให้บรรทัดน้อย | เลือกรูปแบบที่อ่านและตรวจสอบผลได้ง่าย |
| เปลี่ยนทั้งระบบในครั้งเดียว | เริ่มจากส่วนที่ต้องแก้ไขบ่อยและมีการทดสอบรองรับ |
| เชื่อคะแนน static analysis เพียงค่าเดียว | ใช้ร่วมกับการอ่านโค้ด การทดสอบ และบริบทจริง |
หากโค้ดอยู่ในจุดที่ไวต่อประสิทธิภาพ ให้ใช้ profiling หรือ benchmark ประกอบการตัดสินใจ อย่าตัดสินความเร็วจากความสั้นหรือความสวยของโค้ด
#เช็กลิสต์สำหรับ Code Review
- [ ] ชื่อตัวแปรและเมธอดบอกความหมายตรงกับพฤติกรรมจริง
- [ ] หน่วยและชนิดข้อมูลชัดเจนในจุดที่มีผลต่อความถูกต้อง
- [ ] ฟังก์ชันมีเป้าหมายที่อธิบายได้ และระดับรายละเอียดสอดคล้องกัน
- [ ] โครงสร้างเงื่อนไขอ่านตามได้โดยไม่ต้องจำบริบทมากเกินไป
- [ ] กฎที่ใช้ร่วมกันจริงมีแหล่งอ้างอิงที่เหมาะสม
- [ ] คลาสและโมดูลแยกตามเหตุผลที่เปลี่ยนแปลง
- [ ] ไม่มี abstraction หรือความสามารถที่ยังไม่จำเป็น
- [ ] คอมเมนต์อธิบายเหตุผลและยังตรงกับโค้ด
- [ ] ข้อผิดพลาดและผลข้างเคียงถูกจัดการตามสัญญาที่ชัดเจน
- [ ] การทดสอบครอบคลุมพฤติกรรมสำคัญและกรณีขอบ
- [ ] การปรับโครงสร้างยังคงพฤติกรรมตามขอบเขตที่ตกลงไว้
- [ ] รูปแบบโค้ดสอดคล้องกับข้อตกลงของทีม
ทีมสามารถใช้ style guide เป็นฐานสำหรับข้อตกลงเรื่องชื่อและรูปแบบ เช่น Google Java Style Guide [4] โดยปรับให้เหมาะกับโครงการ ตัวอย่างในบทความใช้รูปแบบเพื่อการสอนและไม่ได้อ้างว่าผ่านกฎ Google Style ทุกข้อ
#แบบฝึกหัดสำหรับนักศึกษา
เลือกเมธอดหนึ่งจากโปรเจกต์ที่มีเงื่อนไขซ้อนหรือชื่อที่ยังคลุมเครือ แล้วทำงานต่อไปนี้:
- เขียนอธิบาย input, output, ผลข้างเคียง และกรณีผิดพลาด
- ระบุจุดที่ทำให้เข้าใจยากอย่างน้อยสามจุด พร้อมเหตุผล
- เตรียมการทดสอบพฤติกรรมสำคัญและกรณีขอบ
- ปรับชื่อและโครงสร้างทีละส่วนโดยรันทดสอบระหว่างทาง
- ให้เพื่อนอ่านโค้ดทั้งสองเวอร์ชันและอธิบายกฎที่เข้าใจ
- ส่ง diff พร้อมอธิบายว่าความชัดเจนดีขึ้นตรงไหน และมี trade-off อะไร
เกณฑ์ประเมินควรดูความถูกต้อง ความชัดเจน และเหตุผลในการออกแบบร่วมกัน แทนการให้คะแนนจากจำนวนบรรทัดที่ลดลงเพียงอย่างเดียว
#แหล่งอ้างอิงและอ่านเพิ่มเติม
- Robert C. Martin — The Single Responsibility Principle: การจัดกลุ่มส่วนที่เปลี่ยนด้วยเหตุผลเดียวกัน และแยกส่วนที่เปลี่ยนด้วยเหตุผลต่างกัน
- Martin Fowler — Yagni: การพิจารณาต้นทุนของความสามารถที่สร้างล่วงหน้าก่อนจำเป็น
- Martin Fowler — Definition of Refactoring: ความหมายของ refactoring และการรักษาพฤติกรรมที่สังเกตได้
- Google — Google Java Style Guide: ตัวอย่างข้อตกลงเรื่องการจัดรูปแบบและการตั้งชื่อ Java