Viết code thế nào cho sạch?
Viết code thế nào cho sạch?
Thời
xưa, con người ta thường chú trọng đến hiệu suất chương trình, nên các tên gọi
của các biến, các phương thức thường được đặt một cách khá sơ sài. Càng về sau,
người ta càng nhận ra được giá trị của việc có một đoạn code sạch – đoạn code
được ghi một cách rõ ràng, dễ hiểu – là như thế nào.
Mình
khuyên bạn nên đọc thử cuốn sách Clean Code để có cái nhìn chi tiết hơn. Bài viết
này mình chỉ dựa vào cuốn sách đó để tổng hợp một số ý chính.
Nếu
bạn có một mớ code “bẩn” – đoạn code không được trau chuốt về câu từ, các tên
biến được đặt lung tung, thì theo thời gian, nó sẽ như thế này.
Bạn
thường được gọi là “author” (tác giả) của một chương trình mà bạn viết, vì
chương trình của bạn sẽ viết cho những người khác đọc. Và thậm chí, kể cả bạn đọc
code cũng nhiều hơn cả viết code đấy nhé! Bạn có tin không?
Vào
những năm 80, chúng ta có một trình biên dịch tên là Emacs. Đặc điểm của chương
trình này là ghi nhận lại mọi hành động của bạn. Tác giả của cuốn sách, Robert C.Martin – hay
còn gọi là Uncle Bob, đã thử làm việc trong hàng giờ và xem lại hoạt động của
mình. Và, thật ngạc nhiên, hầu hết thời gian của ông chỉ để dành cho việc lăn
chuột và xem các module khác!
Bob
truy cập vào một module
Ông
lăn xuống tìm một phương thức cần chỉnh sửa
Ông
dừng lại, suy nghĩ
Ôi,
ông ta lại lăn lên đầu để xem xét khai báo biến
Giờ
thì ông ta quay lại chỗ cũ và nhập gì đó
Ooops,
ông ta xóa đoạn mã ổng vừa nhập rồi
Ông
ta lại nhập
Lại
xóa nữa
Ông
ta nhập tiếp một cái gì đó nhưng bất chợt lại xóa
Ông
ta đi tìm phương thức khác, cái mà gọi phương thức ông đang chỉnh sửa, để xem
cách nó gọi như thế nào!
Ông
ta quay lại và gõ y như lúc nãy vừa mới xóa
Ông
ta dừng lại
Ông
ta lại xóa đoạn mã đó nữa
Ông
ta nhìn vào một cửa sổ khác và xem các subclass. Liệu rằng hàm này có
overridden không?
…
Bạn
có thể thấy rằng khoảng hơn 90% anh ta dành ra chỉ để đọc đi đọc lại đoạn code
cũ. Chúng ta hầu như đọc lại đoạn code cũ nhiều hơn là viết
một đoạn code mới. Vì việc đọc chiếm rất nhiều thời gian, nên việc chúng ta
làm đoạn code dễ đọc hơn cũng là cách giúp chúng ta dễ viết hơn.
Mình
nghĩ rằng một đoạn mở đầu ngắn thế này cũng đủ để cho các bạn hiểu tầm quan trọng
của code sạch là gì rồi nhỉ. Giờ chúng ta sẽ tìm hiểu những quy tắc đặt tên
chương trình cho dễ hiểu nhé.
Lưu
ý là các quy tắc ở đây có thể bạn cảm thấy đúng, có thể không. Quan trọng là bản
thân bạn, và những người xung quanh bạn có thể đọc và hiểu được đoạn code của bạn
một cách dễ dàng. Vậy nên là, đừng cứng nhắc quá nhé!
1. Đặt
tên phải thể hiện rõ ý định của bạn
Khi mới bắt đầu học và học về thuật toán,
bạn sẽ dễ bắt gặp các cách đặt tên biến sử dụng các kí tự trong bảng chữ cái
như a, b, c, d,… Tuy nhiên, đó không phải là một cách hay, vì nó không thể hiện
rõ được ý nghĩa của biến, của hàm, của đối tượng mà mình đặt tên đó. Ví dụ,
mình có một chương trình:
public List<Integer>
abc(List<Integer> a){
List<Integer> b = new ArrayList<Integer>();
for(int x: a)
if(x == 0 || x ==
1){
b.add(x);
}
}
return b;
}
Khi bạn đọc vào, bạn có hiểu chương trình
thực sự có ý nghĩa gì không? Đọc đoạn code trên, bạn có cảm thấy thắc mắc rằng:
-
Hàm abc có ý nghĩa gì?
-
Danh sách a chứa những gì?
-
Tại sao lại kiểm tra x = 0 hay x = 1?
Hai con số đó có mục đích gì?
-
Danh sách b là cái gì? Tại sao lại trả về
danh sách b?
Ok, vậy, mình sẽ nói cho mọi người biết
bài toán gì. Đây là bài toán tìm phòng trống để đặt phòng. Danh sách được truyền
vào chính là danh sách các phòng với ba trạng thái: 0 – Không có ai ở cả, 1 –
Có người ở nhưng có thể ở chung được, 2 – Có người ở nhưng không thể ở chung. Một
vị khách muốn thuê phòng và anh nhân viên phải thực hiện phương thức này để xác
định xem những phòng nào anh ta có thể ở được. Ở đây chúng ta sẽ đặt tên các biến
và phương thức lại để nó dễ đọc hơn.
public List<Integer>
getAccommodableRooms(List<Integer> rooms){
List<Integer> accommodableRooms = new ArrayList<Integer>();
for(int room: rooms)
if(room == EMPTY_ROOM
|| room == SHARED_ROOM){
accommodableRooms.add(room);
}
}
return accommodableRooms;
}
Chúng ta đã đã thay đổi các con số bằng một
hằng có khai báo, để tên của nó được thể hiện rõ nét hơn
Bằng cách sử dụng những từ ngữ có thể hiện
rõ mục đích, chúng ta đã giúp cho code dễ đọc hơn. Tuy nhiên, cấu trúc của code
vẫn còn chưa được thay đổi, và nó còn khá sơ sài. Chúng ta sẽ chỉnh sửa lại như
sau.
public List<Room>
getAccommodableRooms(List<Room> rooms){
List<Room> accommodableRooms = new ArrayList<Room>();
for(Room room: rooms)
if(room.isRentable()){
accommodableRooms.add(room);
}
}
return
accommodableRooms;
}
Chúng
ta đã tạo một đối tượng để thể hiện rõ giá trị trả về là một cái “Room” thay vì
là một con số nguyên “integer” (Mặc dù room định nghĩa cơ bản chỉ là mã số
phòng). Tiếp đó, chúng ta dùng hàm room.isRentable()
thay
vì thể hiện ra các con số, để thể hiện rõ mục đích của chúng ta chỉ là kiểm tra
liệu phòng đó có thể thuê được hay không, thay vì phải thể hiện ra bằng các con
số.
Tuy
nhiên, nếu như lạm dụng quá mức sự gói gọn các đoạn code vào trong một phương
thức sẽ làm khó khăn trong việc nhìn thấy được chi tiết của chương trình. Đặc
biệt là khi các bạn làm thuật toán, bạn thường sẽ cố gắng viết để bạn có thể hiểu
càng chi tiết càng tốt. Bạn sẽ cảm thấy rất khó khăn nếu như bạn lướt qua một
giải thuật mà phải mở ra một đống phương thức để xem bên trong nó viết cái gì
(mình đã từng bị vậy). Vậy nên, tóm gọn lại, tùy vào độ chi tiết bạn muốn người khác nhìn thấy mà bạn
sẽ gói gọn nó theo cách phù hợp với mình.
2. Tránh
việc sai lệch thông tin
Okay, giờ bạn đã biết rằng không được đặt
cái tên một cách vô nghĩa, cần phải đặt sao cho nó có thông tin, có ý nghĩa. Thế
nhưng, bạn có bao giờ nghĩ rằng, tên bạn đặt thật sự đúng với ý định của bạn
không? Có bao giờ nó rất dễ để bị hiểu sai ý nghĩa không?
Bạn có hay đặt tên một danh sách các tài
khoản là accountList,
danh sách các học sinh là studentList,…
không? Nghe giống tả mình quá nhỉ haha! Nếu bạn đã từng, thì mình nghĩ bạn nên
tập thay đổi cách gọi khác, vì trong Java thì List còn là một kiểu dữ liệu, nếu
bạn không khai báo danh sách là một List mà đặt tên lại sử dụng accountList,
studentList thì sẽ dễ gây nhầm lẫn lắm đấy. Thay vì thế, bạn có thể đặt tên gọi
là bunchOfAccount
hay đơn giản chỉ là accounts
thôi.
Cẩn thận trong việc đặt tên quá dài. Bạn
nghĩ sao khi đọc một tên biến ghi là XYZUsingForCountNumberOfRooms.
Rồi nào thì XYZUsingForTotalPeopleOfRooms.
Hai đứa này nhìn nó có vẻ giống nhau, và khi đọc mình sẽ phải dừng một lúc để
hiểu ý nghĩa của nó đấy.
Tạo tính nhất quán khi đặt tên cũng khá là
quan trọng. Nếu bạn học Java Swing, bạn sẽ để ý rằng các Label thường sẽ có hậu
tố chung là Lbl như titleLbl,
nameLbl,… Bạn cũng có thể
thấy các hàm lấy giá trị sẽ có từ get, thay đổi giá trị sẽ có từ set, và các
hàm trả về boolean sẽ có từ is như isAccessable,
getName, setName.
Vẫn còn có nhiều quy tắc đặt tên nữa, mà
mình sẽ có thể viết thêm một bài khác (hoặc là không vì nó khá dài). Mình vẫn
khuyến khích các bạn nếu cảm thấy hứng thú, hãy đọc thử cuốn Clean Code để học
hỏi kinh nghiệm và nâng cao khả năng viết code gọn gàng của mình.
Lưu ý rằng bạn phân biệt được code bẩn
code sạch không đồng nghĩa với việc bạn biết cách viết code sạch. Cũng giống
như việc bạn biết tranh nào đẹp không đồng nghĩa với việc bạn biết vẽ tranh tốt.
Cần phải có thời gian luyện tập, kết hợp với việc học hỏi từ những người xung
quanh, sẽ giúp cho cách viết code của bạn trở nên tốt hơn.
Nội dung bài viết thuộc
về Lê Công Diễn. Mang đi nhớ dẫn nguồn.
Người viết: Lê Công Diễn
Mang đi nhớ ghi nguồn
Nhận xét
Đăng nhận xét