Giới thiệu
Đây là những nguyên lý kỹ thuật phần mềm, được trích từ cuốn sách Clean Code của tác giả Robert C. Martin [thường gọi là Uncle Bob] rất thích hợp cho ngôn ngữ PHP. Tài liệu này không phải là sách hướng dẫn về phong cách viết code, mà là hướng dẫn cách làm thế nào để viết phần mềm dễ đọc, dễ sử dụng lại, và dễ cải tiến trong PHP.
Bạn không cần phải tuân theo tất cả các nguyên tắc trong tài liệu này. Đây chỉ đơn giản là những hướng dẫn, nhưng dù sao nó cũng là đúc kết từ nhiều năm kinh nghiệm của tác giả.
Lưu ý: Dù nhiều lập trình viên còn sử dụng PHP 5, nhưng nhiều ví dụ trong đây chỉ chạy được trên PHP 7.1+.
Vì bài viết khá dài nên mình xin tách thành 2 phần, hi vọng bài viết sẽ có ích
Biến
Sử dụng tên biến có ý nghĩa và dễ hiểu
Chưa tốt:
$ymdstr = $moment->format['y-m-d'];
Tốt:
$currentDate = $moment->format['y-m-d'];
Sử dụng cùng từ vựng cho cùng một loại biến
Chưa tốt:
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
Tốt:
getUser[];
Sử dụng cùng từ vựng cho cùng một loại biến
Đặt tên sao cho dễ tìm kiếm [phần 1]
Chưa tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
Tốt:
$json = $serializer->serialize[$data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE];
Sử dụng cùng từ vựng cho cùng một loại biến
Chưa tốt:
// Rồi, 69 nghĩa là cái gì đây? =]]
if [$user->access & 69] {
// ...
}
Tốt:
class User
{
const ACCESS_READ = 6;
const ACCESS_CREATE = 9;
const ACCESS_UPDATE = 69;
const ACCESS_DELETE = 96;
}
if [$user->access & User::ACCESS_UPDATE] {
// edit ...
}
Sử dụng cùng từ vựng cho cùng một loại biến
Đặt tên sao cho dễ tìm kiếm [phần 1]
Chưa tốt:
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*[.+?]\s*[\d{5}]$/';
preg_match[$cityZipCodeRegex, $address, $matches];
saveCityZipCode[$matches[1], $matches[2]];
Tốt:
Sử dụng cùng từ vựng cho cùng một loại biến
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,]+,\s*[.+?]\s*[\d{5}]$/';
preg_match[$cityZipCodeRegex, $address, $matches];
[, $city, $zipCode] = $matches;
saveCityZipCode[$city, $zipCode];
Tốt:
Sử dụng cùng từ vựng cho cùng một loại biến
$currentDate = $moment->format['y-m-d'];
0Đặt tên sao cho dễ tìm kiếm [phần 1]
Thường thì chúng ta sẽ đọc code nhiều hơn viết code. Nên điều quan trọng là code chúng ta viết ra phải dễ đọc và dễ tìm kiếm. Nếu không đặt tên biến có ý nghĩa và làm chương trình dễ hiểu, chúng ta sẽ gây khó cho những lập trình viên khác. Do đó mỗi khi đặt tên biến, hàm thì hãy đặt có ý nghĩa.
Chưa tốt:
$currentDate = $moment->format['y-m-d'];
1Tốt:
Sử dụng cùng từ vựng cho cùng một loại biếnĐặt tên sao cho dễ tìm kiếm [phần 1]
Chưa tốt:
$currentDate = $moment->format['y-m-d'];
3Tốt:
$currentDate = $moment->format['y-m-d'];
4Sử dụng cùng từ vựng cho cùng một loại biến
Đặt tên sao cho dễ tìm kiếm [phần 1]
Chưa tốt:
$currentDate = $moment->format['y-m-d'];
5Tốt:
$currentDate = $moment->format['y-m-d'];
6Sử dụng cùng từ vựng cho cùng một loại biến
Đặt tên sao cho dễ tìm kiếm [phần 1]
Chưa tốt:
$currentDate = $moment->format['y-m-d'];
7Tốt:
$currentDate = $moment->format['y-m-d'];
8Sử dụng cùng từ vựng cho cùng một loại biến
Chưa tốt:
Tốt:
$currentDate = $moment->format['y-m-d'];
9Tốt:
Sử dụng cùng từ vựng cho cùng một loại biến
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
0Tốt:
Sử dụng cùng từ vựng cho cùng một loại biến
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
1Đặt tên sao cho dễ tìm kiếm [phần 1]
Thường thì chúng ta sẽ đọc code nhiều hơn viết code. Nên điều quan trọng là code chúng ta viết ra phải dễ đọc và dễ tìm kiếm. Nếu không đặt tên biến có ý nghĩa và làm chương trình dễ hiểu, chúng ta sẽ gây khó cho những lập trình viên khác. Do đó mỗi khi đặt tên biến, hàm thì hãy đặt có ý nghĩa.
Chưa tốt:
Tốt:
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
2Sử dụng cùng từ vựng cho cùng một loại biếnChuỗi '42' thì phải khác số 42 chứ đúng không.
Tốt:
Sử dụng cùng từ vựng cho cùng một loại biến
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
3Đặt tên sao cho dễ tìm kiếm [phần 1]
Thường thì chúng ta sẽ đọc code nhiều hơn viết code. Nên điều quan trọng là code chúng ta viết ra phải dễ đọc và dễ tìm kiếm. Nếu không đặt tên biến có ý nghĩa và làm chương trình dễ hiểu, chúng ta sẽ gây khó cho những lập trình viên khác. Do đó mỗi khi đặt tên biến, hàm thì hãy đặt có ý nghĩa.
Đặt tên sao cho dễ tìm kiếm [phần 2]
Đặt tên biến dễ hiểu
Chưa tốt:
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
4Tốt:
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
5Sử dụng cùng từ vựng cho cùng một loại biến
Đặt tên sao cho dễ tìm kiếm [phần 1]
Chưa tốt:
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
6Tốt:
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
7Sử dụng cùng từ vựng cho cùng một loại biến
Chưa tốt:
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
8Tốt:
getUserInfo[];
getUserData[];
getUserRecord[];
getUserProfile[];
9Sử dụng cùng từ vựng cho cùng một loại biến
Đặt tên sao cho dễ tìm kiếm [phần 1]
Chưa tốt:
getUser[];
0Tốt:
Sử dụng cùng từ vựng cho cùng một loại biến
getUser[];
1Tốt:
Sử dụng cùng từ vựng cho cùng một loại biến
getUser[];
2Đặt tên sao cho dễ tìm kiếm [phần 1]
Thường thì chúng ta sẽ đọc code nhiều hơn viết code. Nên điều quan trọng là code chúng ta viết ra phải dễ đọc và dễ tìm kiếm. Nếu không đặt tên biến có ý nghĩa và làm chương trình dễ hiểu, chúng ta sẽ gây khó cho những lập trình viên khác. Do đó mỗi khi đặt tên biến, hàm thì hãy đặt có ý nghĩa.
Chưa tốt:
getUser[];
3Tốt:
getUser[];
4Tránh tác dụng phụ
Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.
Vậy nếu bây giờ bạn cần hàm thực hiện các tác dụng phụ đó thì sao. Giống như ví dụ trước, bạn cần ghi vào file. Điều bạn cần làm là tập trung những việc này lại một chỗ. Đừng viết vài hàm và vài lớp chỉ để ghi vào vài file cụ thể. Hãy viết một service để làm điều đó. Một và chỉ một service.
Hãy tránh những sai lầm phổ biến như: chia sẻ trạng thái giữa các object mà không tuân theo cấu trúc nào, sử dụng kiểu dữ liệu có thể thay đổi/bị thay đổi dễ dàng, không tổng hợp các tác dụng phụ có thể xảy ra khi viết hàm.
Chưa tốt:
getUser[];
5Tốt:
getUser[];
6Tránh tác dụng phụ
Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.
Vậy nếu bây giờ bạn cần hàm thực hiện các tác dụng phụ đó thì sao. Giống như ví dụ trước, bạn cần ghi vào file. Điều bạn cần làm là tập trung những việc này lại một chỗ. Đừng viết vài hàm và vài lớp chỉ để ghi vào vài file cụ thể. Hãy viết một service để làm điều đó. Một và chỉ một service.
Hãy tránh những sai lầm phổ biến như: chia sẻ trạng thái giữa các object mà không tuân theo cấu trúc nào, sử dụng kiểu dữ liệu có thể thay đổi/bị thay đổi dễ dàng, không tổng hợp các tác dụng phụ có thể xảy ra khi viết hàm.
Chưa tốt:
getUser[];
7Tốt:
getUser[];
8Tránh tác dụng phụ
getUser[];
9Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.
Vậy nếu bây giờ bạn cần hàm thực hiện các tác dụng phụ đó thì sao. Giống như ví dụ trước, bạn cần ghi vào file. Điều bạn cần làm là tập trung những việc này lại một chỗ. Đừng viết vài hàm và vài lớp chỉ để ghi vào vài file cụ thể. Hãy viết một service để làm điều đó. Một và chỉ một service.
Hãy tránh những sai lầm phổ biến như: chia sẻ trạng thái giữa các object mà không tuân theo cấu trúc nào, sử dụng kiểu dữ liệu có thể thay đổi/bị thay đổi dễ dàng, không tổng hợp các tác dụng phụ có thể xảy ra khi viết hàm.
- Đừng viết hàm globalglobal instance, vì sao lại Chưa tốt? Bởi vì bạn ẩn dependencies của ứng dụng bên trong code của bạn, thay vì thông qua interfaces
- Dùng nhiều hàm global là bad practice với nhiều ngôn ngữ bởi vì có thể gây xung đột với thư viện khác và người sử dụng API của bạn không hề hay biết gì cho đến khi nhận được thông báo lỗi.chúng điều khiển những gì chúng tạo ra và vòng đời của nó
- Hãy xem xét ví dụ sau: bạn sẽ làm gì nếu muốn trả về một mảng.test khó khăn hơn.
- Bạn có thể viết hàm global như
3, nhưng nó có thể xung đột với thư viện khác thực hiện cùng chức năng.// Rồi, 69 nghĩa là cái gì đây? =]] if [$user->access & 69] { // ... }
Tạo instance của lớp
// Rồi, 69 nghĩa là cái gì đây? =]]
if [$user->access & 69] {
// ...
}
4Chưa tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
0Tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
1Tránh tác dụng phụ
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
2Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.
Vậy nếu bây giờ bạn cần hàm thực hiện các tác dụng phụ đó thì sao. Giống như ví dụ trước, bạn cần ghi vào file. Điều bạn cần làm là tập trung những việc này lại một chỗ. Đừng viết vài hàm và vài lớp chỉ để ghi vào vài file cụ thể. Hãy viết một service để làm điều đó. Một và chỉ một service.
Chưa tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
3Tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
4Tránh tác dụng phụ
Chưa tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
5Tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
6Tránh tác dụng phụ
Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.
Chưa tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
7Tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
8Tránh tác dụng phụ
Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.
Vậy nếu bây giờ bạn cần hàm thực hiện các tác dụng phụ đó thì sao. Giống như ví dụ trước, bạn cần ghi vào file. Điều bạn cần làm là tập trung những việc này lại một chỗ. Đừng viết vài hàm và vài lớp chỉ để ghi vào vài file cụ thể. Hãy viết một service để làm điều đó. Một và chỉ một service.
Chưa tốt:
// Oh man, 448 là cái gì vậy?
$result = $serializer->serialize[$data, 448];
9Tốt:
$json = $serializer->serialize[$data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE];
0Tránh tác dụng phụ
Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.
Vậy nếu bây giờ bạn cần hàm thực hiện các tác dụng phụ đó thì sao. Giống như ví dụ trước, bạn cần ghi vào file. Điều bạn cần làm là tập trung những việc này lại một chỗ. Đừng viết vài hàm và vài lớp chỉ để ghi vào vài file cụ thể. Hãy viết một service để làm điều đó. Một và chỉ một service.
Chưa tốt:
$json = $serializer->serialize[$data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE];
1Tốt:
$json = $serializer->serialize[$data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE];
2Tránh tác dụng phụ
Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.
Chưa tốt:
$json = $serializer->serialize[$data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE];
3Tốt:
$json = $serializer->serialize[$data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE];
4Tránh tác dụng phụ
Một hàm sinh ra tác dụng phụ nếu nó thực hiện thêm việc khác ngoài việc lấy giá trị vào và trả về một hoặc nhiều giá trị khác. Tác dụng phụ có thể là viết vào một file nào đó, sửa đổi biến global, hoặc vô tình chuyển hết tiền của bạn cho người lạ nào đó.
//github.com/henryonsoftware/clean-code-php