Code Refactoring (Tái cấu trúc mã nguồn) là những kỹ thuật sắp xếp lại mã nguồn để chúng trở nên tốt hơn mà không làm ảnh hưởng tới hành vi của hệ thống đối với bên ngoài. Có rất nhiều kỹ thuật refactoring khác nhau, mỗi kỹ thuật đôi khi chỉ làm thay đổi một chút nho nhỏ mã nguồn, nhưng những thay đổi nhỏ đó được tích luỹ dần theo thời gian thì tạo nên một ảnh hưởng rất lớn, giúp cho hệ thống của chúng ta trở nên tốt hơn.
“Tốt” hơn nghĩa là thế nào? Nghĩa là chúng sẽ “clean” hơn và “SOLID” hơn.
Refactoring là một trong các nhóm kỹ thuật có liên quan đến nhau và ảnh hưởng đến nhau, bao gồm kiểm thử tự động, TDD, clean code, design pattern… và đều tuân thủ các nguyên lý quan trọng về thiết kế phần mềm.
Chủ đề Live Stream lần này về Code Refactoring sẽ đề cập đến ý nghĩa của refactoring, các kỹ thuật refactoring thông dụng và ứng dụng của chúng trong thực tế. Phiên demo sẽ có các hướng dẫn về việc sử dụng các công cụ để thực hiện các kỹ thuật refactoring và giải thích cụ thể lợi ích của chúng. Nếu bạn đã nghe về Clean Code, SOLID, Design Pattern thì phiên Live Stream lần này là một dịp không thể bỏ qua để hoàn thiện hơn nhóm các kỹ thuật quan trọng này.
4. Tuyên ngôn Agile
Chúng tôi đã phát hiện ra cách phát triển phần mềm tốt hơn bằng cách thực
hiện nó và giúp đỡ người khác thực hiện. Qua công việc này, chúng tôi đã đi đến việc
đánh giá cao:
Cá nhân và sự tương tác hơn là quy trình và công cụ;
Phần mềm chạy tốt hơn là tài liệu đầy đủ;
Cộng tácvới khách hàng hơn là đàm phán hợp đồng;
Phản hồi với các thay đổihơn là bám sát kế hoạch.
Mặc dù các điều bên phải vẫn còn giá trị, nhưng chúng tôi đánh giá cao hơn các mục ở bên trái.
AgileAlliance.org
6. Design is the key,
Planned Design is not …
• Takeuchi & Nonaka: overlapping is better than sequential
Not efficient:
• Time consuming
• No backward
• No “better idea” on the go
Làm thế nào để tổ chức kiểu “Overlapping” như thế này?
7. Simple Design là gì?
• Design sẽ tiến hoá theo quá trình
• KHÔNG cần có design toàn bộ từ đầu. Vừa đủ thôi
• Là một phần của quy trình phát triển
• Chương trình lớn lên thì design sẽ thay đổi theo
• Không phải là chiến thuật “code and fix”
7
Evolve
8. Simplicity
Rationale behind Simple Design
• ”Tạo ra thứ đơn giản nhất có thể chạy được“
• ”Chúng ta sẽ không bao giờ cần tới nó“
• Đầu tư cho mẫu thiết kế
• Hệ thống tối giản
1. Chạy toàn bộ test
2. Rõ mục đích
3. Không trùng lặp
4. Có ít lớp và phương thức nhất có thể
8
9. Thiết kế để Giao tiếp
• Vẽ thiết kế ra để thảo luận trong nhóm
• Chỉ cần đủ để làm rõ giải pháp
• Chỉ nên sử dụng các lưu đồ đơn giản, không mất nhiều công
duy trì
• Giữ minh bạch các lưu đồ
• Treo ở tường
• Khuyến khích mọi người cập nhật
• Để ý xem liệu mọi người có dùng không, hay là vứt xó
9
10. Thiết kế để Xây dựng
• “Phần mềm chạy tốt là thước đo chính về tiến độ”
• Thiết kế sẽ được chuyển thành tính năng chạy được, do đó:
• Design phải chia ra để code được trong nhóm
• Phân tách, interface, giao tiếp giữa các thành phần
• Design phải kiểm thử được
• “Kiến trúc sư cần phải code”
10
11. Refactoring
• Để có thiết kế đơn giản hơn
• Duy trì, cập nhật dễ hơn
• Là một việc bắt buộc phải làm
• Để ý đến mẫu thiết kế và các best practices
11
Image: http://www.testically.org
12. Tái cấu trúc mã nguồn
• Tái cấu trúc mã nguồn là các kỹ thuật cho phép chỉnh sửa mã
nguồn nội bộ mà không làm thay đổi hành vi của hệ thống đối
với bên ngoài
• Tái cấu trúc mã nguồn nhằm mục đích chính:
• Mã nguồn dễ duy trì hơn
• Mã nguồn dễ mở rộng hơn
Lưu ý: Tái cấu trúc mã nguồn không phải luôn luôn giúp tăng hiệu năng
(performance) của thuật toán. Trong một số trường hợp, có thể cần hy sinh hiệu
năng để có được mã nguồn tốt hơn
13. Development Flow
Requirement
Analysis
UI Mocking
•Customer
discussion
Design Draft
•Design
Discussion
Code the
skeleton to
test the
design
Coding in
team
Refactoring
and
Refinement
Build the
increment
$
DevTeamPO
Collaboration:
Steps:
Artifacts:
As a super user,
I want to …
A
B
IDo
Interface IDo{
//TODO …
}
Class A{
//TODO …
}
Class B:IDo{
//TODO …
}
Interface IDo{
//TODO …
}
Class A{
method1(){
//Mr. A codes here
}
}
Class B:IDo{
method1(){
//Mrs. B codes here
}
}
Class C{
}
$
PO
14. Tiến hoá của các Model
com.myapp.Models
com.myapp.Views
com.myapp.Controllers
Sprint 0
updated story
V1
M1
IDo
C1
V1
M1
IDo
C1
V2
M2
Sprint 1 Sprint 2
Product
Backlog
Items burnt
Items burnt
Initial Architecture Model1 Model 2
19. Phương thức dài quá
Dấu hiệu:
• Quá nhiều dòng code
• Có quá nhiều tầng code
• Độ phức tạp cao
Xử lý:
• Tách phương thức
• Sử dụng Parameter Object
• Phân tách các khối lệnh điều
kiện
19
20. Lớp to quá
Dấu hiệu:
• Có quá nhiều trường
• Có quá nhiều phương thức
• Có quá nhiều dòng code
Xử lý:
• Tách lớp
• Tách lớp con
• Tách interface
20
21. Danh sách tham số dài quá
Dấu hiệu:
• Có nhiều hơn 3 hoặc 4 tham
số cho một phương thức
Xử lý:
• Thay thế tham số bằng lời gọi
phương thức
• Giữ nguyên object
• Truyền vào đối tượng
21
22. Mã bị lặp
Dấu hiệu:
• Hai hoặc nhiều đoạn mã nhìn
gần giống nhau
Xử lý:
• Tách phương thức
• Tách lớp cha
• Chuyển thành template method
• Thay thế thuật toán
22
23. Ghi chú
Dấu hiệu:
• Có nhiều ghi chú để giải thích
các đoạn mã
Xử lý:
• Tách biến
• Tách phương thức
• Thay đổi tên phương thức
23
25. Một số kỹ thuật tái cấu trúc
Kỹ thuật đổi tên biến
Kỹ thuật đổi tên phương thức
Kỹ thuật tách biến
Kỹ thuật tách hằng
Kỹ thuật tách phương thức
26. Đổi tên biến và phương thức
• Thay đổi tên biến hoặc phương thức để trở nên tốt hơn: dễ
đọc, có ý nghĩa, thể hiện được ý nghĩa, tuân thủ coding
convention
• Khi đổi tên biến hoặc phương thức cần lưu ý:
• Đổi tên tại vị trí khai báo
• Đổi tên tại tất cả các vị trí có sử dụng biến hoặc phương thức
• Nên sử dụng tính năng của IDE để đổi tên biến hoặc phương
thức
27. Tách biến
• Trong nhiều trường hợp, các biểu thức phức tạp sẽ gây khó
hiểu
• Tách biến (Variable Extraction) là kỹ thuật giúp đơn giản hoá
các biểu thức và giúp dễ hiểu hơn
28.
29.
30. Tách biến: Ví dụ kiểm tra năm nhuận
function isLeapYear(year) {
if(year % 4 == 0){
if (year % 100 == 0){
if(year % 400 == 0)
return true;
} else {
return true;
}
}
return false;
}
function isLeapYear(year) {
varr isDivisibleBy4 = year % 4 == 0;
if(isDivisibleBy4){
varr isDivisibleBy100 = year % 100 == 0;
if (isDivisibleBy100){
varr isDivisibleBy400 = year % 400 == 0;
if(isDivisibleBy400)
return true;
} else {
return true;
}
}
return false;
}
31. Tách hằng
• Trong nhiều trường hợp, các giá trị “thần kỳ” (magic value) sẽ
gây khó khăn cho việc đọc hiểu mã nguồn
• Tách hằng giúp mang lại ý nghĩa cho các giá trị “thần kỳ” và mã
nguồn dễ hiểu hơn
32. Tách hằng: Ví dụ phân quyền dựa vào
role
function isAuthorized(var role){
if(role == 1){
return true;
}
return false;
}
const ROLE_ADMIN = 1;
function isAuthorized(role){
if(role == ROLE_ADMIN){
return true;
}
return false;
}
33. Tách phương thức
• Trong nhiều trường hợp, một phương thức quá dài, quá phức
tạp hoặc xử lý quá nhiều tác vụ sẽ dẫn đến khó hiểu, khó kiểm
soát
• Tách phương thức giúp cho các phương thức dễ đọc hiểu hơn,
dễ kiểm soát hơn
34.
35.
36. Tách phương thức: Ví dụ tính số ngày
function getDaysOfMonth(month, year){
switch (month){
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
return 31;
case 4:
case 6:
case 9:
case 11:
return 30;
…
case 2:
var isLeapYear = false;
if(year % 4 == 0){
if (year % 100 == 0){
if(year % 400 == 0)
isLeapYear = true;
} else {
isLeapYear = true;
}
}
if(isLeapYear){
return 29;
} else {
return 28;
}
default:
return 0;
}
37. function getDaysOfMonth(month, year){
switch (month){
…..
case 4:
case 6:
case 9:
case 11:
return 30;
case 2:
var isLeapYear = isLeapYear(year);
if(isLeapYear){
return 29;
} else {
return 28;
}
default:
return 0;
}
}
function isLeapYear(year) {
var isLeapYear = false;
if(year % 4 == 0){
if (year % 100 == 0){
if(year % 400 == 0)
isLeapYear = true;
} else {
isLeapYear = true;
}
}
return isLeapYear;
}