• Thứ Năm, 02/07/2009 07:55 (GMT+7)

    Refactoring & ứng dụng trong việc cải tiến mã xấu

    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 Metho, 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
    Email: nlphoa@gmail.com
    --------------------------------------
    Tài liệu tham khảo:
    [1] Joseph W. Yoder.
    Refactoring Principles. University of Illinois, 2004
    [2] Martin Fowler.
    Refactoring: Improving the design of existing code. Addison Wesley,1999.
    [3] Bad Smells in Code
    http://sis36.berkeley.edu/projects/streek/agile/bad-smells-in-code.html
    [4] Refactoring HomePage
    http://www.refactoring.com
    [5] The Future of Software Development
    http://www.readwriteweb.com/archives/the_future_of_software_development.php

    ID: A0906_120