Chào tất cả anh em. Ở bài trước (Function trong clean code phần 1) chúng ta nói nhiều về cách triển khai hàm. Đên bài hôm nay chúng ta sẽ tập trung nhiều về tham số (Function Arguments).
Tham số (Function Arguments )
Số lượng tham số lý tưởng trong hàm là 0 (niladic), 1 (monadic), 2 (dyadic). Với 3 tham số (triadic) nên xem xét kỹ lưỡng, tránh được nếu có thể. Còn các trường hợp nhiều hơn thì thực sự không nên dùng.
Tham số còn gây khó khăn cho việc việc viết testcase và kiểm thử phần mềm. Tổ hợp số lượng cách thức truyền tham số vào sẽ tăng dần theo số lượng tham số của hàm. Thật khó khăn khi phải kiểm tra hết toàn bộ các trường hợp đó xem chạy có đúng không.
Tham số đầu ra (out trong C#, hay biến con trỏ, kiểu tham chiếu) khó để hiểu hơn tham số đầu vào. thông thường sẽ xây dựng function với việc đưa vào các tham số và nhận kết quả được return trả về.
Kiểu một tham số phổ biến (Common Monadic Forms)
Có 2 lý do phổ biển để sử dụng hàm 1 tham số:
- Hàm trả về dạng true/false ví dụ như: fileExists(“my_file”)
- Hàm biến đổi tham số đầu vào thành 1 cái gì đó khác và trả về. Ví dụ: InputStream fileOpen(“my_file”) đầu vào có thể là file Path trả về Stream
Một trường hợp ít phổ biển khác nhưng vẫn hay dùng với hàm 1 tham số đó là Event. Với mẫu này thì hàm sẽ có tham số đầu vào là event, và không có kết quả trả về. Thường những hàm này dùng tham số đầu vào để thay đổi trạng thái của hệ thống. Ví dụ : passwordAttemptFailedNtimes(int attempts)
Cố gắng hạn chế các function 1 tham số không nằm trong các trường hợp trên.
Tham số cơ (Flag Arguments)
Function có tham số dạng cờ (bool) thực sự nhìn khá khó chịu. Bản chất của các hàm dạng này là sẽ thực hiện 1 đoạn mã nếu cờ truyền vào là true và thực hiện 1 đoạn mã khác nếu cờ là false. Thông thường xử lý việc này chúng ta tách ra làm 2 hàm riêng biệt.
Function 2 tham số (Dyadic functions)
Hàm 2 tham số khó hiểu hơn hàm 1 tham số. Ví dụ như hàm writeField(name) sẽ dễ hiểu hơn rất nhiều so với hàm writeField(outputStream,name)
Có 1 vài trường hợp chúng ta sử dụng 2 tham số là thích hợp ví dụ
Point p = new Point(0,0)
Việc này hoàn thờn hợp với lẽ tự nhiên. Giả như nếu chỉ là new Point(0) thì còn gây nhạc nhiên hơn cho người đọc rất nhiều.
Hai tham số truyền vào nên là giá trị sắp xếp như là thành phần của 1 giá trị. Hay nói cách khác : Hai tham số phải có sự gắn kết tự nhiên hoặc là sắp xếp của 1 trật tự tự nhiên.
Để xử lý việc này chúng ta có thể tách ra class mới ví dụ như là tạo class mới FieldWriter, trong đó outputStream là thuộc tính của class này, write là method trong class này.\
Funtions 3 tham số (Triads)
Function với 3 tham số khó hơn function 2 tham số rất nhiều. Nếu không thực sự cần thiết, bạn hãy tính toán thật kỹ trước khi sử dụng function 3 tham số này.
Đôi khi hàm 3 tham số được sử dụng như 1 giải pháp chấp nhận được: asertEquals(1.0, amount, .001)
Tham số đầu vào là đối tượng (Argument Objects)
Khi 1 hàm dường như cần nhiều hơn 2 hay 3 tham số, có vẻ như đó là lúc nên wrap các tham số đó vào 1 class.
Xem ví dụ sau:
Circle makeCircle(double x, double y, double radius); // Viết lại là Circle makeCircle(Point center, double radius);
Việc giảm số lượng tham số bằng cách tạo ra object chứa các tham số đó dường như là 1 thủ thuật để lách luật. Nhưng thật sự không hẳn là như vậy, những tham số được gom vào có phần liên quan tới nhau, và có thể là thuộc tính của 1 đối tượng cụ thể nào đó.
Tham số dạng list (Argument list)
Ví dụ như hàm sau:
String.format("%s worked %.2f hours.", name, hours);
Chúng ta sẽ thấy việc tham số truyền vào có thể sẽ được mở rộng thêm nữa. Tuy vậy chúng ta có thể hiểu rằng, các tham số truyền vào như name, hours về mặt bản chất có ý nghĩa như nhau. Khi đó cũng có thể coi hàm String.format này là hàm dạng 2 tham số. Mà bản chất của hàm là như sau
public String format(String format, Object… args)
Động từ và từ khóa: Verbs and Keywords
Chọn 1 cái tên tốt cho function có thể giúp người đọc dễ dàng hiểu mục đích của hàm, cũng như thứ tự tham số truyền vào.
Trong trường hợp monad (1 tham số), function và tham số nên được tổ chức tên dưới dạng cặp động từ/danh từ
Ví dụ
write(name) // mang ý nghĩa rằng bất kì name này là gì, nó đều có thể được viết (write).
nhưng nếu viết là:
writeField(name), // nó cho chúng ta biết “name” là 1 “field”
Thậm chí chúng ta có thể sử dụng tên các tham số để đưa vào tên function giúp làm rõ nghĩa thêm. Ví dụ: assertExpectedEqualsActual(expected,actual)
Việc này khá hữu ích trong việc phải ghi nhớ thứ tự tham số, dù rằng các IDE hiện tại đều hỗ trợ tốt điều này.
Kết thúc phần 2 (Function agurment) anh em nên đắn đo suy nghĩ xem các biến chuyền vào có thực sự cần thiết, hoặc function đó có nên tồn tại hay không khi phải chuyển quá nhiều biến.
Đọc tiếp: Function trong clean code phần cuối.
Bài viết liên quan