Các tham số được đặt tên làm cho mã dễ đọc hơn, khó viết hơn
Khi tôi đang đọc một đoạn mã, các tham số có tên có thể giới thiệu ngữ cảnh giúp mã dễ hiểu hơn. Xem xét ví dụ nhà xây dựng này : Color(1, 102, 205, 170)
. Điều đó có nghĩa là gì? Thật vậy, Color(alpha: 1, red: 102, green: 205, blue: 170)
sẽ dễ đọc hơn nhiều. Nhưng than ôi, Trình biên dịch nói rằng không có ai - nó muốn Color(a: 1, r: 102, g: 205, b: 170)
. Khi viết mã bằng các tham số được đặt tên, bạn dành một lượng thời gian không cần thiết để tìm kiếm tên chính xác - việc quên tên chính xác của một số tham số sẽ dễ dàng hơn so với việc quên thứ tự của chúng.
Điều này một lần làm tôi đau đầu khi sử dụng DateTime
API có hai lớp anh chị em cho các điểm và thời lượng với giao diện gần như giống hệt nhau. Trong khi DateTime->new(...)
chấp nhận một second => 30
đối số, DateTime::Duration->new(...)
mong muốn seconds => 30
và tương tự cho các đơn vị khác. Vâng, nó hoàn toàn có ý nghĩa, nhưng điều này cho tôi thấy rằng các tham số được đặt tên ≠ dễ sử dụng.
Tên xấu thậm chí không làm cho nó dễ đọc hơn
Một ví dụ khác làm thế nào các tham số được đặt tên có thể xấu có lẽ là ngôn ngữ R. Đoạn mã này tạo ra một biểu đồ dữ liệu:
plot(plotdata$n, plotdata$mu, type="p", pch=17, lty=1, bty="n", ann=FALSE, axes=FALSE)
Bạn thấy hai đối số vị trí cho các hàng dữ liệu x và y và sau đó là danh sách các tham số được đặt tên. Có nhiều tùy chọn khác với mặc định và chỉ những tùy chọn được liệt kê có mặc định tôi muốn thay đổi hoặc chỉ định rõ ràng. Một khi chúng ta bỏ qua rằng mã này sử dụng các số ma thuật và có thể hưởng lợi từ việc sử dụng enums (nếu R có bất kỳ!), Vấn đề là nhiều tên tham số này không thể giải mã được.
pch
thực sự là nhân vật cốt truyện, glyph sẽ được vẽ cho mọi điểm dữ liệu. 17
là một vòng tròn trống rỗng, hoặc một cái gì đó như thế.
lty
là loại đường. Đây 1
là một dòng rắn.
bty
là loại hộp. Đặt nó để "n"
tránh một hộp được vẽ xung quanh cốt truyện.
ann
kiểm soát sự xuất hiện của các chú thích trục.
Đối với một người không biết ý nghĩa của mỗi từ viết tắt, các tùy chọn này khá khó hiểu. Điều này cũng tiết lộ lý do tại sao R sử dụng các nhãn này: Không phải là mã tự ghi, nhưng (là ngôn ngữ được gõ động) làm khóa để ánh xạ các giá trị tới các biến chính xác của chúng.
Thuộc tính của thông số và chữ ký
Chữ ký chức năng có thể có các thuộc tính sau:
- Các đối số có thể được đặt hàng hoặc không có thứ tự,
- được đặt tên hoặc không được đặt tên,
- yêu cầu hoặc tùy chọn.
- Chữ ký cũng có thể bị quá tải bởi kích thước hoặc loại,
- và có thể có kích thước không xác định với varargs.
Các ngôn ngữ khác nhau hạ cánh tại các tọa độ khác nhau của hệ thống này. Trong C, các đối số được sắp xếp, không được đặt tên, luôn luôn được yêu cầu và có thể là các biến. Trong Java, tình huống tương tự, ngoại trừ chữ ký có thể bị quá tải. Trong Mục tiêu C, chữ ký được sắp xếp, đặt tên, bắt buộc và không thể bị quá tải vì đó chỉ là đường cú pháp xung quanh C.
Các ngôn ngữ được gõ động với varargs (giao diện dòng lệnh, Perl, Nhận) có thể mô phỏng các tham số được đặt tên tùy chọn. Các ngôn ngữ có quá tải kích thước chữ ký có một cái gì đó giống như các tham số tùy chọn vị trí.
Làm thế nào để không thực hiện các tham số được đặt tên
Khi nghĩ về các tham số được đặt tên, chúng ta thường giả sử các tham số có tên, tùy chọn, không có thứ tự. Thực hiện những điều này là khó khăn.
Các tham số tùy chọn có thể có các giá trị mặc định. Chúng phải được chỉ định bởi hàm được gọi và không nên được biên dịch thành mã gọi. Mặt khác, mặc định không thể được cập nhật mà không biên dịch lại tất cả các mã phụ thuộc.
Bây giờ một câu hỏi quan trọng là làm thế nào các đối số thực sự được truyền cho hàm. Với các tham số được sắp xếp, các đối số có thể được truyền vào một thanh ghi hoặc theo thứ tự vốn có của chúng trên ngăn xếp. Khi chúng tôi loại trừ các thanh ghi trong giây lát, vấn đề là làm thế nào để đưa các đối số tùy chọn không có thứ tự lên ngăn xếp.
Đối với điều đó, chúng ta cần một số thứ tự trên các đối số tùy chọn. Nếu mã khai báo bị thay đổi thì sao? Vì thứ tự không liên quan, việc sắp xếp lại trong khai báo hàm không nên thay đổi vị trí của các giá trị trên ngăn xếp. Chúng ta cũng nên xem xét nếu thêm một tham số tùy chọn mới là có thể. Từ góc độ người dùng, điều này có vẻ như vậy, bởi vì mã không sử dụng tham số đó trước đây vẫn sẽ hoạt động với tham số mới. Vì vậy, điều này không bao gồm các thứ tự như sử dụng thứ tự trong khai báo hoặc sử dụng thứ tự chữ cái.
Cũng xem xét điều này dưới ánh sáng của phân nhóm và Nguyên tắc thay thế Liskov - trong đầu ra được biên dịch, các hướng dẫn tương tự sẽ có thể gọi phương thức trên một kiểu con với các tham số có tên mới và trên siêu kiểu.
Triển khai có thể
Nếu chúng ta không thể có một thứ tự dứt khoát, vì vậy chúng ta cần một số cấu trúc dữ liệu không có thứ tự.
Việc thực hiện đơn giản nhất là chỉ cần truyền tên của các tham số cùng với các giá trị. Đây là cách các param được đặt tên được mô phỏng trong Perl hoặc với các công cụ dòng lệnh. Điều này giải quyết tất cả các vấn đề mở rộng được đề cập ở trên, nhưng có thể là một sự lãng phí không gian lớn - không phải là một tùy chọn trong mã quan trọng về hiệu năng. Ngoài ra, việc xử lý các thông số có tên này bây giờ phức tạp hơn nhiều so với việc đơn giản là bật các giá trị ra khỏi ngăn xếp.
Trên thực tế, các yêu cầu không gian có thể được giảm bằng cách sử dụng gộp chuỗi, có thể giảm so sánh chuỗi sau với so sánh con trỏ (trừ khi không thể đảm bảo rằng chuỗi tĩnh thực sự được gộp lại, trong trường hợp đó, hai chuỗi sẽ phải được so sánh trong chi tiết).
Thay vào đó, chúng ta cũng có thể vượt qua một cấu trúc dữ liệu thông minh hoạt động như một từ điển của các đối số được đặt tên. Đây là giá rẻ ở phía người gọi, vì bộ phím được biết tĩnh tại vị trí cuộc gọi. Điều này sẽ cho phép tạo ra một hàm băm hoàn hảo hoặc để tính toán trước một trie. Callee vẫn sẽ phải kiểm tra sự tồn tại của tất cả các tên tham số có thể có phần đắt tiền. Một cái gì đó như thế này được sử dụng bởi Python.
Vì vậy, nó quá đắt trong hầu hết các trường hợp
Nếu một hàm với các tham số được đặt tên là có thể mở rộng đúng cách, thì có thể giả sử một thứ tự dứt khoát. Vì vậy, chỉ có hai giải pháp:
- Đặt thứ tự của các thông số được đặt tên là một phần của chữ ký và không cho phép thay đổi sau này. Điều này hữu ích cho mã tự viết tài liệu, nhưng không giúp với các đối số tùy chọn.
- Truyền cấu trúc dữ liệu khóa-giá trị cho callee, sau đó phải trích xuất thông tin hữu ích. Điều này rất tốn kém khi so sánh, và thường chỉ thấy trong các ngôn ngữ kịch bản mà không chú trọng đến hiệu suất.
Cạm bẫy khác
Các tên biến trong khai báo hàm thường có một số ý nghĩa bên trong và không phải là một phần của giao diện - ngay cả khi nhiều công cụ tài liệu vẫn sẽ hiển thị chúng. Trong nhiều trường hợp, bạn muốn các tên khác nhau cho một biến nội bộ và đối số được đặt tên tương ứng. Các ngôn ngữ không cho phép chọn tên hiển thị bên ngoài của tham số đã đặt tên sẽ không nhận được nhiều trong số chúng nếu tên biến không được sử dụng trong bối cảnh gọi.
Một vấn đề với các mô phỏng của các đối số được đặt tên là thiếu kiểm tra tĩnh ở phía người gọi. Điều này đặc biệt dễ quên khi chuyển từ điển đối số (nhìn vào bạn, Python). Điều này rất quan trọng vì việc chuyển từ điển là một cách giải quyết phổ biến, ví dụ như trong JavaScript : foo({bar: "baz", qux: 42})
. Ở đây, không thể kiểm tra tĩnh các loại giá trị cũng như sự tồn tại hoặc vắng mặt của một số tên nhất định.
Mô phỏng các tham số được đặt tên (Trong các ngôn ngữ được nhập tĩnh)
Đơn giản chỉ cần sử dụng các chuỗi làm khóa và bất kỳ đối tượng nào làm giá trị đều không hữu ích khi có trình kiểm tra kiểu tĩnh. Tuy nhiên, các đối số được đặt tên có thể được mô phỏng bằng các cấu trúc hoặc nghĩa đen của đối tượng:
// Java
static abstract class Arguments {
public String bar = "default";
public int qux = 0;
}
void foo(Arguments args) {
...
}
/* using an initializer block */
foo(new Arguments(){{ bar = "baz"; qux = 42; }});