Những đoạn Code "thúi" vi phạm các nguyên tắc design và gây ảnh hưởng tiêu cực tới sản phẩm của bạn.
Sau đây là 10 ví dụ tiêu biểu của 10 kiểu code "thúi" trong Java
#1 Interface chứa toàn hằng số
Dấu hiệu: rất dễ nhận biết, bất kỳ interface nào có các biến khai báo dạng static và không kèm theo method nào, đó là interface "thúi"
Khắc phục: viết lại các hằng số này dưới dạng Enums
Ví dụ tiêu biểu: interface java.io.ObjectStreamConstant
#2 Class chỉ chứa biến toàn cục - global variable class
class Balls {
public static long balls = 0;
}
Hãy xem 3 dòng code trên, class này chỉ có một trường với modifier là public và gắn thêm keyword "static". Nói một cách ngắn gọn, class này chỉ có biến toàn cục public. Như vậy, biến này dễ dàng bị sửa đổi trực tiếp bởi các tác nhân ngoài class
#3 Global function class
Dấu hiệu: một public class chỉ có duy nhất một public static method, không có các trường dữ liệu hay method khác.
public class GlobalFunctionClass{
public static exampleMethod(){
/// some code
}
}
////////// invoke this method:
public class OtherClass(){
public static void main(){
GlobalFunctionClass.exampleMethod();
}
}
#4 Publicy Exposed Field - Biến "lộ thiên"
Dấu hiệu: một public class với các biến kiểu public non-final, non-static và không có method nào (ngoại trừ optional constructor). Việc bảo trì các class có những biến "lộ thiên" như thế này là rất khó khăn.
Nói có sách, mách có chứng, nguyên văn trong cuốn Effective Java có đoạn: "Several classes in the Java platform libraries violate the advice that public classes should not expose fields directly. Prominent examples include the Point and Dimension classes in the java.awt package. Rather than examples to be emulated, these classes should be regarded as cautionary tales."
Tạm dịch: Ngay cả các class trong thư viện của Java cũng vi phạm nguyên tắc: public class không nên "trưng bày" các field của nó ra (tức không để các field dưới dạng public). Ví dụ tiêu biểu cho sự vi phạm này là 2 class Point và Dimension trong gói java.awt.
#5 Orphaned Abstract - thừa kế kiểu "mồ côi"
Dấu hiệu: một class được abstract từ cha, nhưng nguồn gốc class cha không rõ ràng.
Một abstract class chỉ phát huy đầy đủ khả năng nếu nó thừa kế từ class cha "đúng chuẩn". Nếu abstract class không tỏ ra hữu ích, nó có thể bị gỡ bỏ.
#6 Quên implement Interface
Dấu hiệu: một class implement tất cả method của một interface nhưng thiếu cú pháp "implement" ở đầu class.
Hậu quả: object tạo ra bởi class mới này vừa không được thừa kế từ interface theo chủ ý ban đầu, vừa không có tính đa hình.
#7 Clone Class
Dấu hiệu: một clone class có thể khác tên với class nguồn nhưng nội dung bên trong, từ tên biến, tên hàm.... thì giống hệt.
Khắc phục: Nếu hai hoặc nhiều class có chung kiểu dữ liệu và hành vi thì ta cần viết một class cha và cho 2 class kia kế thừa tư class cha này.
#8 Lonely Class
Rất hiếm khi chúng ta gặp một class đơn lẻ, không có ràng buộc cũng như không có dấu hiệu "cộng tác" với các class khác. Những trường hợp này thường là hậu quả của một lỗi thiết kế.
#9 Abstract Leaf
Hãy hình dung các class thừa kế nhau như một gia phả dòng họ trong thế giới thực. Root - gốc cây được xem như cụ tổ, mức dưới root có ông-bà, rồi dưới nữa có bố-mẹ... Như vậy, nếu tương quan vào thế giới lập trình, root - gốc cây sẽ là class có mức abstract cao nhất, càng xa root thì mức abstract càng thấp đi. Những class ở mức lá (không có con) thì không có tính trừu tượng.
Nếu bạn thấy code của bất kỳ ai có class mức lá cũng được trừu tượng hóa thì họ đã mắc một lỗi design cơ bản.
#10 Tagged Class
Tagged class là sản phẩm của các lập trình viên hướng cấu trúc. Thay vì viết các class theo cây thừa kế và tận dụng tính đa hình thì họ lại define các class như là struct, sử dụng enum để liệt kê các type và áp dụng switch-case để phân biệt các type trong từng phép toán.
Khái niệm "tag class" được trình bày trong cuốn Effective Java như sau: "Tag class là một class mà các instance của nó thường có 2 hoặc nhiều "flavor", kèm theo mỗi instance là một dòng tag để nhận diện "flavor" của instance đó.
Để dễ dàng nhận diện tag class, ta có thể đối chiếu theo các tiêu chí sau:
- Có các enum (hoặc các hằng số là số nguyên) để ngụ ý "flavour" của instance
- Một trường để lưu giá trị của enum/int. Thông thường, trường này được set giá trị bởi constructor.
- Một lệnh switch trong một hoặc nhiều method có đoạn code thực thi phụ thuộc vào giá trị tag.
Có rất nhiều tool thống kê và phân tích lỗi trong code Java theo mẫu có sẵn (FindBugs, PMD...). Tuy nhiên các tool tìm được code "thúi" thì hầu như chưa có mặt trên bản đồ. Hi vọng chúng sẽ xuất hiện sớm.
Tài liệu tham khảo:
- [1] Effective Java - 2nd Edition - Joshua Bloch, Addision-Wesley
- [2] "Refactoring for Software Design Smell: managing technical debt" - Girish Suryanarayana, Garnesh Samarthyam, Tushar Sharma.
Bài viết được dịch từ Dzone
Bình luận