(Post 26/03/2010) Phương thức phát triển phần mềm linh hoạt bắt đầu xuất hiện vào đầu những năm 90 với mục tiêu là phần mềm phải có khả năng biến đổi, phát triển và tiến hóa theo thời gian mà không cần phải làm lại từ đầu. Phương thức này được thực hiện dựa hai kỹ thuật chính đó là refactoring (tái cấu trúc mã nguồn) và testing (kiểm thử). Bài viết này giới thiệu tổng quan về kỹ thuật refactoring và ứng dụng trong việc cải tiến các đoạn mã xấu trong các ngôn ngữ lập trình hướng đối tượng hiện đại (.NET & Java) nhằm nâng cao chất lượng của phần mềm. TỔNG QUAN VỀ REFACTORING Định nghĩa Có hai khái niệm khi tiếp cận thuật ngữ refactoring: - Sự thay đổi cấu trúc bên trong của phần mềm, làm cho phần mềm dễ hiểu và ít tốn chi phí khi cần thay đổi cập nhật nhưng không làm thay đổi các hành vi ứng xử bên ngoài.
- Tái cấu trúc lại phần mềm thông qua việc áp dụng các bước cải tiến mà không làm thay đổi hành vi ứng xử bên ngoài.
Như vậy refactoring làm cho chương trình dễ đọc và khi cần thiết có thể cập nhật vẫn không làm thay đổi hoặc có nhưng không đáng kể đến các hành vi ứng xử bên ngoài của phần mềm. Vì vậy phần mềm sẽ thực thi và xử lý các chức năng như trước mà người dùng không cảm nhận về những sự thay đổi này. Hiệu quả của refactoring trong qui trình phát triển phần mềm - Cải thiện thiết kế phần mềm: Cấu trúc chương trình trở nên rõ ràng và logic. Đảm bảo những hạn chế thấp nhất trong quá trình phát triển và cập nhật
- Mã nguồn phần mềm dễ hiểu và tinh gọn: chuẩn hóa trong việc lập trình và cực tiểu hóa dòng lệnh
- Thuận lợi để phát hiện và hạn chế lỗi: Cấu trúc rõ ràng, dễ phát hiện lỗi (debugging)
- Đẩy nhanh quá trình phát triển phần mềm: thông qua các hiệu quả mà nó mang lại (khả năng dùng lại, tăng tính tiến hóa, gần gũi với người dùng,...)
Ngày nay refactoring là một tiêu chuẩn viết mã của mọi lập trình viên họ cần được huấn luyện để tuân thủ: như quy tắc đặt tên biến, khi viết mã nguồn áp dụng pattern nào, xây dựng unit test ra sao ... Refactoring và tối ưu hóa hiệu năng xử lý có điểm chung là chỉ thay đổi cấu trúc bên trong mà không làm thay đổi hành vi của các thành phần phần mềm. Tuy nhiên mục tiêu của chúng khác nhau, tối ưu hóa hiệu năng xử lý thường làm cho đoạn chương trình khó hiểu hơn, nhưng cần phải thực hiện để tăng tốc độ xử lý các tác vụ đặc biệt. Khi nào cần thực hiện refactoring mã nguồn của một chương trình Một lời khuyên từ các nhà phát triển phần mềm hiện đại là trong tất cả các trường hợp, chúng ta đều phải dành thời gian cho việc refactoring. Đây là công việc mà chúng ta phải thực hiện thường xuyên trong chu kì phát triển và bảo dưỡng phần mềm trong các tình huống: - Khi thêm chức năng mới
- Trong quá trình kiểm tra và sửa lỗi (debugging)
- Khi duyệt chương trình từ mã nguồn do người khác viết
Danh mục các kỹ thuật refactoring STT | Kỹ thuật Refactoring | Diễn giải/Mục đích sử dụng | 1 | Add Parameter | Bổ sung tham số cho phương thức | 2 | Change Signature | Thay đổi tính chất/đặc trưng của phương thức | 3 | Change Value/Reference to Reference/Value | Hoán chuyển qua lại giữa giá trị và tham chiếu | 4 | Collapse Hierarchy | Hợp nhất thành một lớp nếu 2 lớp cha và con (kế thừa) có ít sự khác biệt. | 5 | Convert Method/ Property to Property/ Method | Chuyển đổi qua lại giữa phương thức và đặc tính lớp | 6 | Consolidate Conditional Expression | Tạo mới một phương thức từ việc hợp nhất các biểu thức điều kiện | 7 | Consolidate Duplicate Conditional Fragments | Hợp nhất và di chuyển các đoạn trùng lặp ra bên ngoài thân câu lệnh điều kiện | 8 | Decompose Conditional | Tạo mới phương thức từ các câu điều kiện rẽ nhánh | 9 | Duplicate Observed Data | Đa hình dữ liệu | 10 | Encapsulate Field | Chuyển đổi thuộc tính chung thành riêng | 11 | Extract Method/Class/ Interface/ Hierarchy | Trích xuất và định nghĩa mới phương thức/lớp/giao diện/quan hệ kế thừa | 12 | Form Template Method | Đồng nhất các phương thức ở lớp con và dịch chuyển lên lớp cha | 13 | Hide Delegate | Tạo các phương thức trung gian | 14 | Inline Method/Temp/Class | Hợp nhất các phương thức/biến tạm/lớp lại với nhau | 15 | Introduce Foreign Method | Tạo ra một phương thức trong một lớp client có tham số là một đại diện của một lớp server. | 16 | Introduce Explaining Variable | Thay thế kết quả của biểu thức hoặc một phần của biểu thức vào biến tạm. | 17 | Introduce Local Extension | Tạo một lớp mới chứa các phương thức mở rộng đi kèm với một lớp con hoặc một lớp bao | 18 | Introduce Null Object | Thay thế giá trị Null bởi đối tượng Null | 19 | Move Method/Field | Dịch chuyển phương thức/thuộc tính giữa các lớp | 20 | Promote Local Variable to Parameter | Chuyển biến cục bộ thành tham số của phương thức | 21 | Push Down Field/Method | Phân rã thuộc tính/phương thức về lớp con | 22 | Pull Up Field/Method | Tổng quát hóa thuộc tính/phương thức lên lớp cha | 23 | Remove Redundant Parameter/Field/ Conditional/Middle Man | Xóa bỏ các dữ liệu dư thừa(tham số của phương thức, thuộc tính lớp, điều kiện rẽ nhánh, phương thức trung gian) | 24 | Rename Parameter/ Variable/ Method | Đổi tên tham số/ biến/ phương thức | 25 | Replace Array with Object | Thay thế mảng bởi đối tượng với phần tử của mảng tương ứng với thuộc tính của đối tượng | 26 | Replace Conditional with Polymorphism | Thay thế phép so sánh điều kiện bởi tính chất đa hình của phương thức | 27 | Replace Constructor with Factory Method | Thay thế hàm dựng bởi phương thức sản xuất | 28 | Replace Data Value with Object | Thay thế giá trị dữ liệu bằng đối tượng | 29 | Replace Inheritance/Delegation with Delegation/ Inheritance | Hoán chuyển qua lại giữa tính chất kế thừa và ủy quyền giữa hai lớp | 30 | Replace Magic Number with Symbolic Constant | Sử dụng biến hằng thay cho giá trị số tường minh | 31 | Replace Parameter with Method | Rút gọn tham số trong phương thức | 32 | Replace Record with Data Class | Thay thế kiểu dữ liệu bảng ghi bởi lớp dữ liệu | 33 | Replace Subclass with Fields | Biến đổi các phương thức ở các thuộc tính siêu lớp và rút gọn các lớp con | 34 | Replace Temp with Query | Chuyển đổi biểu thức thành phương thức. | 35 | Split Temporary Variable | Tạo mới các biến tạm riêng biệt tương ứng với từng đoạn mã | 36 | Self Encapsulate Field | Tạo ra các phương thức dùng để truy xuất gián tiếp các thuộc tính. |
MÃ XẤU VÀ GIẢI PHÁP CẢI TIẾN Khái niệm về mã xấu Mã xấu hay lỗi cấu trúc là những dấu hiệu tồn tại trong mã nguồn của chương trình tiềm ẩn khả năng xảy ra lỗi trong quá trình hoạt động. Các dấu hiệu có thể là: chương trình thiết kế không logic, các phân đoạn mã nguồn có cấu trúc không đồng nhất và khó hiểu, mã nguồn trùng lắp và dư thừa, tên hàm và biến khó kiểm soát, lớp và phương thức phức tạp... Các mã xấu thường gặp và giải pháp cải tiến Dựa trên kinh nghiệm nhiều năm lập trình và nghiên cứu về refactoring, hai chuyên gia Kent Beck và Marting Fowler đã đề xuất một tập các mã xấu thường gặp và giải pháp cải tiến dựa trên kỹ thuật refactoring. STT | Đặc tả mã xấu | Kỹ thuật refactoring áp dụng | 1 | Duplicated Code (Trùng lặp mã) | Extract Method/Class, Pull Up Method, Form Template Method, Substitute Algorithm | 2 | Long Method (Phương thức phức tạp) | Extract Method, Replace Temp with Query, Introduce Parameter Object, Replace Method with Method Object, Decompose Conditional | 3 | Large Class (Qui mô lớp lớn) | Extract Class/SubClass, Extract Interface, Duplicate Observed Data | 4 | Long Parameter List (Danh sách tham số quá dài) | Replace Parameter with Method, Preserve Whole Object, Introduce Parameter Object | 5 | Divergent Change (Cấu trúc lớp ít có tính khả biến) | Extract Class | 6 | Shotgun Surgery (Thiết kế lớp không hợp lý và phân rã) | Move Method,Move Field, Inline Class | 7 | Feature Envy (Phân bố phương thức giữa các lớp không hợp lý) | Extract Method, Move Method | 8 | Data Clumps (Gom cụm dữ liệu) | Extract Class, Introduce Parameter Object, Preserve Whole Object | 9 | Primitive Obsession (Khả năng thể hiện của lớp bị hạn chế) | Replace Data Value with Object, Replace Type Code with Class, Replace Type Code with Subclasse, Introduce Parameter Object, Replace Array with Object | 10 | Switch Statements (Khối lệnh rẽ hướng không hợp lý) | Extract Method, Move Method, Replace Type Code with Subclasses/State/Strateg, Replace Conditional with Polymorphism, Replace Parameter with Explicit Methods, Introduce Null Object | 11 | Lazy Class (Lớp được định nghĩa không cần thiết) | Collapse Hierarchy, Inline Class | 12 | Speculative Generality (Cấu trúc bị thiết kế dư thừa) | Collapse Hierarchy, Inline Class, Remove Parameter, Rename Method | 13 | Temporary Field (Đặc tả các thuộc tính không cần thiết) | Extract Class, Introduce Null Object | 14 | Message Chains (Chuỗi phương thức liên hoàn khó kiểm soát) | Extract Method, Move Method | 15 | Middle Man (Quan hệ ủy quyền không hợp lý/logic) | Remove Middle Man, Inline Classs Replace Delegation with Inheritance, | 16 | Inapproprite Intimacy (Cấu trúc thành phần riêng không hợp lý) | Move Method, Move Field | 17 | Alternative Classes with Different Interfaces (Đặc tả lớp không rõ ràng) | Rename Method, Move Method | 18 | Incomplete Library Class (Thư viện lớp chưa được hoàn chỉnh) | Introduce Foregin Method, Introduce Local Extension | 19 | Data Class (Lớp dữ liệu độc lập) | Encapsulate Field, Encapsulate Collection, Remove Setting Method, Move Method, Extract Method, Hide Method | 20 | Refused Bequest (Quan hệ kế thừa không hợp lý/logic) | Push Down Method, Push Down Field, Replace Inheritance with Delegation | 21 | Comments (Chú thích không cần thiết) | |
Ví dụ minh họa Ví dụ 1: Hai lớp con Salesman và Engineer đồng kế thừa từ lớp cha Employee có phương thức getName bị trùng lặp => Sử dụng Pull Up Method Ví dụ 2: Sử dụng kỹ thuật Extract Method để làm ngắn phương thức bằng cách tạo ra một phương thức mới từ việc trích chọn một số đoạn mã từ phương thức ban đầu. Ví dụ 3: Phương thức của một lớp (Class 1) sử dụng nhiều chức năng (thuộc tính và phương thức) của một lớp khác (Class 2) hơn chính nó thì sử dụng Move Method để dịch chuyển phương thức đó sang lớp kia. Ví dụ 4: Sử dụng Replace Inheritance with Delegation để tái cấu trúc một quan hệ kế thừa. Ví dụ 5: Sử dụng kỹ thuật Promote Local Variable to Parameter để thay thế một biến cục bộ thành tham số của một phương thức. Ví dụ 6: Một trong những nguyên tắc trong lập trình để chương trình trở nên sáng sủa và dễ hiểu là cách đặt tên biến sao cho dễ nhớ và đặc tả được mục đích sử dụng biến. Trong trường hợp này, nên dùng kỹ thuật Rename Variables để đổi tên biến sr thành strBuffer. GIẢI PHÁP VÀ CÔNG CỤ HỖ TRỢ RERACTORING Kịch bản giải pháp triển khai Một kịch bản tổng thể cho việc triển khai xây dựng một tiện ích hỗ trợ kỹ thuật refactoring để dò tìm và cải tiến mã xấu có khả năng tích hợp trong các môi trường phát triển ứng dụng được đặc tả như sau: Một số công cụ tiện ích hỗ trợ việc dò tìm và cải tiến mã xấu (plug-in) STT | N.Ngữ hỗ trợ | Kỹ thuật refactoring áp dụng | 1 | .NET (C/C++, C#, VB/ASP.NET) | Refactor in Visual Studio 2003/2005 (http://msdn.microsoft.com/en-us/library/ms379618(VS.80).aspx) | 2 | Visual Assit X for Visual Studio .NET (http://www.wholetomato.com/default.asp) | 3 | C# Refactory for Visual Studio .NET (http://www.xtreme-simplicity.net/index.cfm?PageID=3) | 4 | .NET Refactoring for C# & VB.NET (http://www.knowdotnet.com/articles/netrefactorproducthome.html) | 5 | CodeIT.Once for .NET (http://submain.com/products/codeit.once.aspx) | 6 | JetBrains ReSharper (http://www.jetbrains.com/resharper/) | 7 | DevExpress Refactor!™ Pro (http://www.devexpress.com/Products/Visual_Studio_Add-in/Refactoring/) | 8 | JustCode! (http://www.omnicore.com/en/justcode.htm) | 9 | Java | JetBrains IntelliJ IDE (http://www.jetbrains.com/idea/index.html) | 10 | Elipse IDE for Java Developers (http://www.eclipse.org/) | 11 | JRefactory (http://jrefactory.sourceforge.net/) | 12 | JBuilder (http://www.codegear.com/products/jbuilder) |
Nhiêu Lập Hòa Tài liệu tham khảo: (theo PC World VN) |