- 1. Refactoring ต้องรักษาอะไรไว้บ้าง
- 2. ตารางเลือกเทคนิค
- 3. Extract Method — แยกกลุ่มงานเป็นเมธอด
- 4. Inline Method — รวมเนื้อหาเมธอดกลับสู่จุดเรียก
- 5. Extract Variable — ตั้งชื่อให้ส่วนของนิพจน์
- 6. Inline Temp — ยุบตัวแปรพักค่าที่ไม่จำเป็น
- 7. Replace Temp with Query — ย้ายการคำนวณเป็นเมธอดคืนค่า
- 8. Split Temporary Variable — แยกตัวแปรตามหน้าที่
- 9. Remove Assignments to Parameters — เก็บค่าที่ปรับแล้วในตัวแปรภายใน
- 10. Replace Method with Method Object — สร้างออบเจ็กต์สำหรับงานคำนวณ
- 11. Substitute Algorithm — เปลี่ยนขั้นตอนโดยรักษาสัญญาเดิม
- 12. เทคนิคที่ดูตรงข้ามกัน เลือกอย่างไร
- 13. ขั้นตอนฝึกปฏิบัติ
- 14. ประเด็นสำหรับการสอนและ Code Review
- แหล่งอ้างอิง
เมธอดที่ดูแลรักษาง่ายควรสื่อความตั้งใจของผู้เขียนได้ชัดเจน ทั้งชื่อ ขั้นตอน และการใช้ตัวแปร เมื่ออ่านโค้ดแล้วต้องไล่แกะรายละเอียดจำนวนมาก กลุ่มเทคนิค Composing Methods ช่วยปรับโครงสร้างภายในเมธอดและแบ่งงานให้เหมาะสม โดยรักษาพฤติกรรมที่ผู้เรียกสังเกตได้ไว้เหมือนเดิม

บทความนี้อธิบาย 9 เทคนิคตามหมวด Composing Methods ของ Refactoring.Guru พร้อมตัวอย่าง Java ที่เขียนขึ้นใหม่เพื่อการเรียนรู้ ตัวอย่างเป็นส่วนของคลาส ให้ใส่แต่ละตัวอย่างในคลาสแยกกัน และเลือกใช้เวอร์ชันก่อนหรือหลังทีละเวอร์ชัน ตัวอย่างไม่ได้ระบุเวอร์ชัน Java ล่าสุด และไม่ได้ผ่านการคอมไพล์ในสภาพแวดล้อมที่จัดทำบทความ
#1. Refactoring ต้องรักษาอะไรไว้บ้าง
การปรับโครงสร้างมีเป้าหมายให้โค้ดอ่าน เข้าใจ และแก้ไขได้ง่ายขึ้น โดยพฤติกรรมเดิมยังคงอยู่ เช่น ค่าที่คืนกลับ ข้อยกเว้น ลำดับการบันทึกข้อมูล และจำนวนครั้งที่เรียกบริการภายนอกตามสัญญาของระบบ
ก่อนแก้ไขควรมี regression tests ที่อธิบายพฤติกรรมปัจจุบัน เปลี่ยนครั้งละหนึ่งเรื่อง แล้วรันชุดทดสอบที่เกี่ยวข้อง หากพบข้อผิดพลาดของกฎธุรกิจ ให้แยกการแก้พฤติกรรมออกจากการปรับโครงสร้าง เพื่อให้ตรวจสอบผลของแต่ละการเปลี่ยนแปลงได้
เป้าหมายคือทำให้ความหมายชัดเจน ไม่มีกฎว่าทุกเมธอดต้องสั้นที่สุด หรือทุกนิพจน์ต้องถูกแยกเป็นเมธอด
#2. ตารางเลือกเทคนิค
| เทคนิค | สัญญาณที่พบ | แนวทางหลัก |
|---|---|---|
| Extract Method | โค้ดกลุ่มหนึ่งทำงานย่อยที่ตั้งชื่อได้ | แยกเป็นเมธอดและเรียกใช้แทน |
| Inline Method | เมธอดเพิ่มขั้นตอนการตามอ่านโดยไม่ได้เพิ่มความหมาย | นำเนื้อหากลับไปไว้ที่จุดเรียก |
| Extract Variable | นิพจน์หรือเงื่อนไขอ่านยาก | ตั้งชื่อตัวแปรให้ผลลัพธ์ย่อย |
| Inline Temp | ตัวแปรพักค่าเรียบง่ายและไม่ได้ช่วยสื่อความหมาย | ใช้นิพจน์แทนตัวแปร |
| Replace Temp with Query | การคำนวณควรใช้ซ้ำผ่านเมธอด | ย้ายการคำนวณไปยังเมธอดคืนค่า |
| Split Temporary Variable | ตัวแปรเดียวรับหลายหน้าที่ | แยกตัวแปรตามความหมาย |
| Remove Assignments to Parameters | พารามิเตอร์ถูกเขียนทับในเมธอด | ใช้ตัวแปรภายในเก็บค่าที่ปรับแล้ว |
| Replace Method with Method Object | เมธอดยาวและตัวแปรเกี่ยวพันกันจนแยกยาก | สร้างออบเจ็กต์สำหรับการคำนวณ |
| Substitute Algorithm | ขั้นตอนเดิมเข้าใจยากหรือมีวิธีที่เหมาะกว่า | เปลี่ยนอัลกอริทึมภายใต้สัญญาเดิม |
#3. Extract Method — แยกกลุ่มงานเป็นเมธอด
ปัญหา: เมธอดเดียวมีรายละเอียดหลายระดับ เช่น ทั้งจัดรูปแบบและแสดงผล
แนวทาง: เลือกกลุ่มคำสั่งที่มีหน้าที่เดียว ตั้งชื่อให้สื่อว่างานนั้นทำอะไร แล้วส่งข้อมูลที่ต้องใช้ผ่านพารามิเตอร์ หากมีผลลัพธ์ให้คืนกลับผ่าน return
#ก่อนปรับ
static void printReceipt(String customerName, double total) {
System.out.println("=== RECEIPT ===");
System.out.println("Customer: " + customerName);
System.out.println("Total: " + total);
}
#หลังปรับ
static void printReceipt(String customerName, double total) {
printHeader();
printDetails(customerName, total);
}
private static void printHeader() {
System.out.println("=== RECEIPT ===");
}
private static void printDetails(String customerName, double total) {
System.out.println("Customer: " + customerName);
System.out.println("Total: " + total);
}
เมธอดหลักแสดงลำดับงาน ส่วนรายละเอียดอยู่ในเมธอดย่อย ตัวอย่างสั้นนี้ใช้สาธิตการแยกขั้นตอน ในงานจริงไม่จำเป็นต้องแยก printHeader() หากชื่อใหม่ไม่ได้ช่วยให้เข้าใจงานมากขึ้น
ข้อควรระวัง: ตรวจว่าตัวแปรใดถูกอ่านหรือแก้ไขในส่วนที่จะย้าย และรักษาลำดับ side effects เช่น การพิมพ์หรือเขียนไฟล์
#4. Inline Method — รวมเนื้อหาเมธอดกลับสู่จุดเรียก
ปัญหา: การเรียกผ่านเมธอดอีกชั้นทำให้ต้องตามอ่าน แต่ชื่อเมธอดไม่ได้อธิบายเพิ่ม
แนวทาง: ใช้เนื้อหาของเมธอดแทนการเรียก เมื่อไม่มีจุดเรียกเหลือและไม่กระทบสัญญาภายนอกจึงลบเมธอดเดิม
#ก่อนปรับ
static int priority(int failedAttempts) {
return check(failedAttempts) ? 2 : 1;
}
private static boolean check(int failedAttempts) {
return failedAttempts >= 3;
}
#หลังปรับ
static int priority(int failedAttempts) {
return failedAttempts >= 3 ? 2 : 1;
}
เงื่อนไขสั้นและตรงไปตรงมา จึงอ่านได้ทันทีโดยไม่ต้องเปิดเมธอด check() อีกแห่ง แต่หากชื่อเป็นแนวคิดธุรกิจที่มีความหมาย เช่น requiresManualReview() การเก็บเมธอดไว้ก็อาจช่วยให้ระบบเข้าใจง่ายกว่า
ข้อควรระวัง: อย่า inline โดยละเลยการ override หรือ dynamic dispatch และอย่าลบ public API ที่ผู้ใช้งานภายนอกยังเรียกอยู่ การย้ายเนื้อหาต้องรักษาจำนวนครั้งและลำดับการประเมินอาร์กิวเมนต์ด้วย
#5. Extract Variable — ตั้งชื่อให้ส่วนของนิพจน์
ปัญหา: ผู้อ่านต้องตีความสูตรหรือเงื่อนไขซ้ำทุกครั้ง
แนวทาง: สร้างตัวแปรที่ชื่ออธิบายความหมายของผลลัพธ์ โดยวางใกล้จุดใช้งาน
#ก่อนปรับ
static boolean eligible(int quantity, double unitPrice, int memberYears) {
return quantity * unitPrice >= 2000 && memberYears >= 2;
}
#หลังปรับ
static boolean eligible(int quantity, double unitPrice, int memberYears) {
final double subtotal = quantity * unitPrice;
final boolean meetsSpendingThreshold = subtotal >= 2000;
final boolean isLongTermMember = memberYears >= 2;
return meetsSpendingThreshold && isLongTermMember;
}
ชื่อทำให้เห็นว่ามีเงื่อนไขด้านยอดซื้อและอายุสมาชิก เหมาะกับสูตรที่มีหลายส่วนหรือค่าที่ต้องอธิบายความหมาย
ข้อควรระวัง: การย้ายนิพจน์ออกจาก && หรือ || อาจทำให้ส่วนที่เดิมถูกข้ามด้วย short-circuit กลับถูกประเมิน เช่น user != null && user.isActive() จะแยกเรียก user.isActive() ล่วงหน้าไม่ได้ ตัวอย่างนี้ใช้การคำนวณจาก primitive จึงไม่มีปัญหาดังกล่าว
#6. Inline Temp — ยุบตัวแปรพักค่าที่ไม่จำเป็น
ปัญหา: ตัวแปรเก็บนิพจน์ง่าย ๆ เพียงเพื่อคืนค่าทันที และชื่อไม่ได้ช่วยอธิบาย
แนวทาง: นำนิพจน์ไปใช้ตรงจุดที่อ้างถึงตัวแปร แล้วลบตัวแปร
#ก่อนปรับ
static int itemCount(int boxes, int itemsPerBox) {
int value = boxes * itemsPerBox;
return value;
}
#หลังปรับ
static int itemCount(int boxes, int itemsPerBox) {
return boxes * itemsPerBox;
}
ข้อควรระวัง: ตัวแปรที่ใช้ครั้งเดียวก็อาจมีประโยชน์ หากชื่อช่วยอธิบายสูตร นอกจากนี้ value = fetchData() ที่ถูกอ้างถึงหลายครั้งไม่ควรถูกแทนด้วยการเรียก fetchData() ทุกจุด เพราะอาจเพิ่มการทำงาน ผลข้างเคียง หรือได้คนละค่า
#7. Replace Temp with Query — ย้ายการคำนวณเป็นเมธอดคืนค่า
ปัญหา: การคำนวณที่มีความหมายถูกผูกไว้กับตัวแปรภายในเมธอด ทำให้ส่วนอื่นใช้ซ้ำได้ยาก
แนวทาง: ย้ายการคำนวณไปเป็น query method แล้วเรียกเมธอดนั้นจากจุดใช้งาน โดย query ที่นำมาใช้ซ้ำควรไม่มีผลข้างเคียง
#ก่อนปรับ
static final class OrderPrice {
private final int quantity;
private final double unitPrice;
OrderPrice(int quantity, double unitPrice) {
this.quantity = quantity;
this.unitPrice = unitPrice;
}
double discountedTotal() {
double subtotal = quantity * unitPrice;
return subtotal >= 2000 ? subtotal * 0.90 : subtotal;
}
}
#หลังปรับ
static final class OrderPrice {
private final int quantity;
private final double unitPrice;
OrderPrice(int quantity, double unitPrice) {
this.quantity = quantity;
this.unitPrice = unitPrice;
}
double subtotal() {
return quantity * unitPrice;
}
double discountedTotal() {
return subtotal() >= 2000 ? subtotal() * 0.90 : subtotal();
}
}
เมธอด subtotal() ทำให้การคำนวณใช้ซ้ำได้ ตัวอย่างมีฟิลด์ที่ไม่เปลี่ยนค่าและคำนวณง่าย การเรียกซ้ำจึงไม่เปลี่ยนผล แต่จำนวนครั้งที่คำนวณเพิ่มขึ้น
ข้อควรระวัง: หาก query ทำงานแพง เช่น รวมข้อมูลจำนวนมาก หรืออ่านข้อมูลที่เปลี่ยนระหว่างการเรียก ควรประเมินต้นทุนและความสอดคล้องของข้อมูล การเก็บผล query ไว้ในตัวแปรเฉพาะรอบงานยังเป็นทางเลือกที่เหมาะสมได้
#8. Split Temporary Variable — แยกตัวแปรตามหน้าที่
ปัญหา: ตัวแปรเดียวเปลี่ยนความหมายไปมาภายในเมธอด
แนวทาง: สร้างตัวแปรใหม่ให้แต่ละแนวคิด ใช้ชื่อที่ตรงกับข้อมูล และใช้ final เมื่อไม่ต้องเปลี่ยนค่า
#ก่อนปรับ
static void printRectangle(double width, double height) {
double result = 2 * (width + height);
System.out.println("Perimeter: " + result);
result = width * height;
System.out.println("Area: " + result);
}
#หลังปรับ
static void printRectangle(double width, double height) {
final double perimeter = 2 * (width + height);
System.out.println("Perimeter: " + perimeter);
final double area = width * height;
System.out.println("Area: " + area);
}
ผู้อ่านไม่ต้องตามว่าขณะนี้ result หมายถึงค่าประเภทใด
ข้อควรระวัง: ตัวนับลูปและตัวสะสม เช่น sum += value มีหน้าที่เดิมตลอดการใช้งาน การเปลี่ยนค่าหลายครั้งจึงไม่ได้เป็นเหตุให้ต้องแยกตัวแปรเสมอไป
#9. Remove Assignments to Parameters — เก็บค่าที่ปรับแล้วในตัวแปรภายใน
ปัญหา: เมธอดเขียนทับพารามิเตอร์จนแยกค่าเริ่มต้นกับค่าระหว่างคำนวณได้ยาก
แนวทาง: ใช้ตัวแปรภายในสำหรับค่าที่ผ่านการปรับ และเก็บพารามิเตอร์ไว้สื่อถึงอินพุตเดิม
#ก่อนปรับ
static double applyDiscount(double price, boolean member) {
if (member) {
price = price * 0.90;
}
return price;
}
#หลังปรับ
static double applyDiscount(final double price, boolean member) {
double discountedPrice = price;
if (member) {
discountedPrice = price * 0.90;
}
return discountedPrice;
}
ใน Java การส่งพารามิเตอร์เป็น pass-by-value การกำหนด price ใหม่ในตัวอย่างแรกไม่ได้เปลี่ยนตัวแปร primitive ของผู้เรียก ประโยชน์ของการปรับครั้งนี้จึงอยู่ที่ความชัดเจนของข้อมูลภายในเมธอด
สำหรับพารามิเตอร์ออบเจ็กต์ การกำหนด reference ใหม่กับการแก้สถานะของออบเจ็กต์เป็นคนละเรื่อง final ป้องกันการกำหนดพารามิเตอร์ใหม่ แต่ไม่ได้ทำให้ออบเจ็กต์ immutable และการคัดลอก reference ไปยังตัวแปรใหม่ก็ไม่ได้สร้างสำเนาออบเจ็กต์
#10. Replace Method with Method Object — สร้างออบเจ็กต์สำหรับงานคำนวณ
ปัญหา: เมธอดมีตัวแปรระหว่างทางเกี่ยวพันกันมาก การใช้ Extract Method ทันทีอาจสร้างพารามิเตอร์จำนวนมาก
แนวทาง: ย้ายงานไปยังคลาสเฉพาะ ส่งอินพุตผ่าน constructor และย้ายสถานะระหว่างคำนวณที่จำเป็นไปเป็นฟิลด์ จากนั้นแบ่งเป็นเมธอดย่อยภายในคลาสนั้น
#ก่อนปรับ
static double netPay(int hours, double hourlyRate, double allowance) {
int regularHours = Math.min(hours, 40);
int overtimeHours = Math.max(hours - 40, 0);
double regularPay = regularHours * hourlyRate;
double overtimePay = overtimeHours * hourlyRate * 1.5;
double grossPay = regularPay + overtimePay + allowance;
double taxablePay = Math.max(grossPay - 1000, 0);
double tax = taxablePay * 0.10;
return grossPay - tax;
}
#หลังปรับ
static double netPay(int hours, double hourlyRate, double allowance) {
return new PayCalculation(hours, hourlyRate, allowance).compute();
}
private static final class PayCalculation {
private final int hours;
private final double hourlyRate;
private final double allowance;
private double grossPay;
private double taxablePay;
PayCalculation(int hours, double hourlyRate, double allowance) {
this.hours = hours;
this.hourlyRate = hourlyRate;
this.allowance = allowance;
}
double compute() {
calculateGrossPay();
calculateTaxablePay();
return grossPay - taxablePay * 0.10;
}
private void calculateGrossPay() {
int regularHours = Math.min(hours, 40);
int overtimeHours = Math.max(hours - 40, 0);
double regularPay = regularHours * hourlyRate;
double overtimePay = overtimeHours * hourlyRate * 1.5;
grossPay = regularPay + overtimePay + allowance;
}
private void calculateTaxablePay() {
taxablePay = Math.max(grossPay - 1000, 0);
}
}
ตัวอย่างย่อแสดงการย้ายสถานะการคำนวณออกจากเมธอดเดิม สำหรับงานจริงที่สั้นเท่านี้ Extract Method อาจเพียงพอ Method Object เหมาะเมื่อการเกี่ยวพันของตัวแปรทำให้การแยกเมธอดแบบปกติยุ่งยากจริง ๆ
ข้อควรระวัง: สร้างออบเจ็กต์การคำนวณใหม่สำหรับแต่ละงานตามตัวอย่าง ไม่ใช้สถานะ mutable ร่วมกันระหว่างคำขอพร้อมกัน คลาสนี้เป็นออบเจ็กต์ช่วยคำนวณ ไม่จำเป็นต้องพัฒนาเป็นระบบ Command Pattern เพิ่มเติม
สูตรเงินเดือนเป็นกติกาสมมติเพื่อสาธิตโครงสร้างเท่านั้น ตัวอย่างใช้
doubleเพื่อให้อ่านง่าย ระบบการเงินจริงควรกำหนดความแม่นยำและกฎการปัดเศษอย่างชัดเจน เช่น ออกแบบด้วยBigDecimalการเปลี่ยนชนิดตัวเลขควรทำเป็นอีกงานหนึ่ง เพราะอาจเปลี่ยนผลลัพธ์เดิม
#11. Substitute Algorithm — เปลี่ยนขั้นตอนโดยรักษาสัญญาเดิม
ปัญหา: อัลกอริทึมเดิมยาว เข้าใจยาก หรือมีไลบรารีที่แสดงเจตนาได้ชัดกว่า
แนวทาง: ระบุอินพุต เอาต์พุต และกรณีขอบให้ชัด แล้วแทนที่เนื้อหาภายในเมธอดด้วยวิธีใหม่ที่ให้พฤติกรรมตามสัญญาเดิม
#ก่อนปรับ
static int maximum(int[] values) {
if (values.length == 0) {
throw new IllegalArgumentException("values must not be empty");
}
int max = values[0];
for (int i = 1; i < values.length; i++) {
if (values[i] > max) {
max = values[i];
}
}
return max;
}
#หลังปรับ
static int maximum(int[] values) {
return java.util.Arrays.stream(values)
.max()
.orElseThrow(() ->
new IllegalArgumentException("values must not be empty"));
}
ทั้งสองแบบคืนค่าสูงสุด ไม่แก้ไขอาร์เรย์ และโยน IllegalArgumentException พร้อมข้อความเดิมเมื่ออาร์เรย์ว่าง อินพุต null ทำให้ทั้งสองแบบเกิด NullPointerException แต่รายละเอียดข้อความหรือ stack trace อาจต่างกัน หากระบบถือรายละเอียดเหล่านี้เป็นสัญญา ต้องตรวจเพิ่ม
ทั้งสองวิธีใช้เวลา O(n) ตัวอย่างนี้สาธิตการแทนขั้นตอนด้วยไลบรารีเพื่อสื่อเจตนา ไม่ได้ยืนยันว่า Stream เร็วกว่า loop
ข้อควรระวัง: ตรวจลำดับผลลัพธ์ ค่าซ้ำ null ข้อยกเว้น การแก้ข้อมูลต้นฉบับ และ precision ตามประเภทงาน หากเป้าหมายคือความเร็ว ให้ benchmark ด้วยข้อมูลที่เป็นตัวแทนของการใช้งานจริง
#12. เทคนิคที่ดูตรงข้ามกัน เลือกอย่างไร
| ตัวเลือก | เลือกฝั่งแรกเมื่อ | เลือกฝั่งหลังเมื่อ |
|---|---|---|
| Extract Method / Inline Method | ชื่อเมธอดซ่อนรายละเอียดและอธิบายงานได้ดี | การเรียกเพิ่มภาระตามอ่านโดยไม่มีความหมายเพิ่ม |
| Extract Variable / Inline Temp | ชื่อตัวแปรช่วยตีความสูตร | นิพจน์เดิมชัดเจนและตัวแปรไม่เพิ่มข้อมูล |
| Extract Variable / Replace Temp with Query | ต้องการตั้งชื่อผลลัพธ์ในบริบทเฉพาะจุด | ต้องการให้การคำนวณมีชื่อและใช้ซ้ำผ่านเมธอด |
| Extract Method / Method Object | ส่งข้อมูลผ่านพารามิเตอร์และคืนค่าได้ตรงไปตรงมา | สถานะระหว่างทางเกี่ยวพันมากจนแยกยาก |
การเลือกขึ้นอยู่กับบริบทเดียวกันได้หลายแบบ เช่น subtotal() อาจเป็น query ที่ดี ขณะที่ final double subtotal = order.subtotal(); ก็เหมาะเมื่ออยากใช้ค่าหนึ่งชุดตลอดรอบงาน
#13. ขั้นตอนฝึกปฏิบัติ
- เลือกเมธอดที่อ่านยากหนึ่งเมธอด และเขียนพฤติกรรมที่ต้องรักษาไว้
- เตรียม regression tests โดยเน้นอินพุตปกติและขอบเขตเงื่อนไข
- ระบุปัญหาเฉพาะ เช่น ตัวแปรหลายหน้าที่ หรือนิพจน์ไม่สื่อความหมาย
- เลือกหนึ่งเทคนิค เปลี่ยนขนาดเล็ก แล้วรันทดสอบ
- อ่านโค้ดจากมุมผู้ดูแลคนถัดไป ตรวจว่าชื่อช่วยให้เข้าใจหรือไม่
- ทำซ้ำเท่าที่จำเป็น และบันทึกแต่ละการเปลี่ยนแปลงให้ตรวจสอบได้
กรณีทดสอบแนะนำสำหรับตัวอย่างในบทความ (เป็นแผนทดสอบ ยังไม่ได้รัน):
| ตัวอย่าง | อินพุตที่ควรครอบคลุม | สิ่งที่ตรวจ |
|---|---|---|
| Receipt | ชื่อทั่วไป ชื่อว่าง และ null |
ข้อความและลำดับบรรทัดเหมือนเดิม |
| Priority | จำนวนครั้ง 2, 3, 4 | ค่าก่อนขอบเขตและที่ขอบเขต |
| Eligibility | ยอด 1999, 2000 และอายุสมาชิก 1, 2 | ต้องผ่านทั้งสองเงื่อนไข |
| Item count | จำนวนกล่อง 0 และค่าบวก | ผลคูณเหมือนเดิม |
| Order price | ยอด 1999, 2000, 2001 | ส่วนลดเริ่มที่ยอด 2000 |
| Rectangle | ด้านเป็น 0 และค่าบวก | เส้นรอบรูป พื้นที่ และลำดับพิมพ์ |
| Discount | สมาชิกและบุคคลทั่วไป | ส่วนลดและค่าของตัวแปรฝั่งผู้เรียก |
| Pay calculation | ชั่วโมง 0, 40, 41 และรายได้คร่อม 1000 | OT และฐานภาษีสมมติ |
| Maximum | ค่าติดลบ ค่าซ้ำ ค่าเดียว อาร์เรย์ว่าง และ null |
ผลลัพธ์ ข้อยกเว้น และอาร์เรย์ไม่ถูกแก้ |
สำหรับตัวเลขทศนิยม ให้กำหนดเกณฑ์เปรียบเทียบที่เหมาะสม และตรวจว่ามีการเปลี่ยนลำดับคำนวณจนกระทบผลหรือไม่ การเพิ่ม validation ใหม่ระหว่าง refactoring ก็อาจเปลี่ยนพฤติกรรม จึงควรแยกพิจารณา
#14. ประเด็นสำหรับการสอนและ Code Review
- ชื่อใหม่บอกเจตนาหรือเพียงย้ายความซับซ้อนไปอีกที่?
- การใช้ตัวแปรสะท้อนความหมายเดียวตลอดช่วงชีวิตหรือไม่?
- การประเมินนิพจน์ จำนวนครั้งที่เรียก และ side effects ยังเหมือนเดิมหรือไม่?
- การปรับแตะ public API, inheritance หรือ shared state หรือไม่?
- มีหลักฐานจาก regression tests รองรับการรักษาพฤติกรรมหรือยัง?
Composing Methods ช่วยให้แต่ละขั้นตอนและข้อมูลภายในเมธอดมีความหมายชัดขึ้น การแยกหรือรวมโค้ดควรตัดสินจากความเข้าใจที่เพิ่มขึ้นและพฤติกรรมที่ตรวจสอบได้
#แหล่งอ้างอิง
คำอธิบายแนวคิดเรียบเรียงภาษาไทยจากหน้าต่อไปนี้ ส่วนตัวอย่าง Java และข้อสังเกตในการประยุกต์เขียนเพิ่มเติมสำหรับบทความนี้
- Extract Method — Refactoring.Guru
- Inline Method — Refactoring.Guru
- Extract Variable — Refactoring.Guru
- Inline Temp — Refactoring.Guru
- Replace Temp with Query — Refactoring.Guru
- Split Temporary Variable — Refactoring.Guru
- Remove Assignments to Parameters — Refactoring.Guru
- Replace Method with Method Object — Refactoring.Guru
- Substitute Algorithm — Refactoring.Guru