Đối với các ngôn ngữ đương đại, đó chỉ là cú pháp cú pháp; theo một cách hoàn toàn không biết ngôn ngữ, nó còn hơn thế nữa.
Câu trả lời trước đây chỉ đơn giản là nó nhiều hơn đường cú pháp, nhưng nếu bạn thấy trong các bình luận, Falco đã đưa ra quan điểm rằng có một phần của câu đố mà các ngôn ngữ đương đại dường như bị thiếu; họ không trộn lẫn quá tải phương thức với xác định động của hàm nào sẽ gọi trong cùng một bước. Điều này sẽ được làm rõ sau.
Đây là lý do tại sao nó nên được nhiều hơn.
Xem xét một ngôn ngữ hỗ trợ cả hai phương thức nạp chồng và các biến chưa được kiểm tra. Bạn có thể có các nguyên mẫu phương thức sau:
bool someFunction(int arg);
bool someFunction(string arg);
Trong một số ngôn ngữ, bạn có thể sẽ bị từ chức để biết tại thời điểm biên dịch mà một trong số các ngôn ngữ này sẽ được gọi bởi một dòng mã nhất định. Nhưng trong một số ngôn ngữ, không phải tất cả các biến đều được nhập (hoặc tất cả chúng đều được nhập ngầm Object
hoặc là bất cứ thứ gì), vì vậy hãy tưởng tượng xây dựng một từ điển có khóa ánh xạ tới các giá trị của các loại khác nhau:
dict roomNumber; // some hotels use numbers, some use letters, and some use
// alphanumerical strings. In some languages, built-in dictionary
// types automatically use untyped values for their keys to map to,
// so it makes more sense then to allow for both ints and strings in
// your code.
Bây giờ, nếu bạn muốn áp dụng someFunction
cho một trong những số phòng đó thì sao? Bạn gọi đây là:
someFunction(roomNumber[someSortOfKey]);
Được someFunction(int)
gọi, hay được someFunction(string)
gọi? Ở đây bạn thấy một ví dụ trong đó đây không phải là các phương pháp trực giao hoàn toàn, đặc biệt là trong các ngôn ngữ cấp cao hơn. Ngôn ngữ phải tìm ra - trong thời gian chạy - mà một trong số chúng phải gọi, vì vậy nó vẫn phải coi những thứ này ít nhất là cùng một phương thức.
Tại sao không chỉ đơn giản là sử dụng các mẫu? Tại sao không chỉ đơn giản là sử dụng một đối số chưa được kiểm tra?
Linh hoạt và kiểm soát hạt mịn hơn. Đôi khi, sử dụng các mẫu / đối số chưa được kiểm tra là một cách tiếp cận tốt hơn, nhưng đôi khi chúng không như vậy.
Bạn phải suy nghĩ về các trường hợp, ví dụ, bạn có thể có hai chữ ký phương thức, mỗi chữ ký lấy một int
và một string
đối số, nhưng trong đó thứ tự là khác nhau trong mỗi chữ ký. Bạn rất có thể có một lý do chính đáng để làm điều này, vì việc thực hiện mỗi chữ ký có thể làm phần lớn giống nhau, nhưng chỉ với một sự thay đổi hơi khác nhau; việc đăng nhập có thể khác nhau, ví dụ. Hoặc thậm chí nếu họ làm điều tương tự chính xác, bạn có thể tự động thu thập thông tin nhất định từ chỉ thứ tự các đối số được chỉ định. Về mặt kỹ thuật, bạn chỉ có thể sử dụng các câu lệnh chuyển đổi giả để xác định loại của từng đối số được truyền vào, nhưng điều đó trở nên lộn xộn.
Vì vậy, đây là ví dụ tiếp theo thực hành lập trình xấu?
bool stringIsTrue(int arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
bool stringIsTrue(Object arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
bool stringIsTrue(string arg)
{
if (arg == "0")
{
return false;
}
else
{
return true;
}
}
Vâng, bởi và lớn. Trong ví dụ cụ thể này, nó có thể ngăn ai đó cố gắng áp dụng điều này cho một số loại nguyên thủy nhất định và lấy lại hành vi bất ngờ (có thể là một điều tốt); nhưng hãy giả sử tôi viết tắt mã ở trên và trên thực tế, bạn có quá tải cho tất cả các loại nguyên thủy, cũng như cho Object
s. Sau đó, đoạn mã tiếp theo này thực sự phù hợp hơn:
bool stringIsTrue(untyped arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
Nhưng điều gì sẽ xảy ra nếu bạn chỉ cần sử dụng điều này cho int
s và string
s, và nếu bạn muốn nó trở về đúng dựa trên các điều kiện đơn giản hơn hoặc phức tạp hơn thì sao? Sau đó, bạn có một lý do tốt để sử dụng quá tải:
bool appearsToBeFirstFloor(int arg)
{
if (arg.digitAt(0) == 1)
{
return true;
}
else
{
return false;
}
}
bool appearsToBeFirstFloor(string arg)
{
string firstCharacter = arg.characterAt(0);
if (firstCharacter.isDigit())
{
return appearsToBeFirstFloor(int(firstCharacter));
}
else if (firstCharacter.toUpper() == "A")
{
return true;
}
else
{
return false;
}
}
Nhưng này, tại sao không chỉ cung cấp cho các chức năng đó hai tên khác nhau? Bạn vẫn có cùng số lượng kiểm soát chi tiết, phải không?
Bởi vì, như đã nêu trước đây, một số khách sạn sử dụng số, một số sử dụng chữ cái và một số sử dụng hỗn hợp số và chữ:
appearsToBeFirstFloor(roomNumber[someSortOfKey]);
// will treat ints and strings differently, without you having to write extra code
// every single spot where the function is being called
Đây vẫn không chính xác là cùng một mã chính xác mà tôi sẽ sử dụng trong cuộc sống thực, nhưng nó sẽ minh họa điểm tôi đang làm tốt.
Nhưng ... Đây là lý do tại sao nó không hơn đường cú pháp trong các ngôn ngữ đương đại.
Falco nêu quan điểm trong các ý kiến rằng các ngôn ngữ hiện tại về cơ bản không trộn lẫn quá tải phương thức và lựa chọn chức năng động trong cùng một bước. Cách trước đây tôi hiểu một số ngôn ngữ nhất định để làm việc là bạn có thể quá tải appearsToBeFirstFloor
trong ví dụ trên, và sau đó ngôn ngữ sẽ xác định trong thời gian chạy phiên bản của hàm sẽ được gọi, tùy thuộc vào giá trị thời gian chạy của biến không được gõ. Sự nhầm lẫn này một phần xuất phát từ việc làm việc với các loại ngôn ngữ ECMA, như ActionScript 3.0, trong đó bạn có thể dễ dàng chọn ngẫu nhiên hàm nào được gọi trên một dòng mã nhất định khi chạy.
Như bạn có thể biết, ActionScript 3 không hỗ trợ nạp chồng phương thức. Đối với VB.NET, bạn có thể khai báo và đặt các biến mà không chỉ định một kiểu rõ ràng, nhưng khi bạn cố gắng chuyển các biến này thành đối số cho các phương thức bị quá tải, nó vẫn không muốn đọc giá trị thời gian chạy để xác định phương thức nào sẽ gọi; thay vào đó, nó muốn tìm một phương thức với các đối số kiểu Object
hoặc không có kiểu hoặc một cái gì đó tương tự. Vì vậy, ví dụ int
so với string
ở trên cũng không hoạt động trong ngôn ngữ đó. C ++ có các vấn đề tương tự, vì khi bạn sử dụng một cái gì đó như con trỏ void hoặc một số cơ chế khác như thế, nó vẫn yêu cầu bạn phân tán kiểu thủ công trong thời gian biên dịch.
Vì vậy, như tiêu đề đầu tiên nói ...
Đối với các ngôn ngữ đương đại, đó chỉ là cú pháp cú pháp; theo một cách hoàn toàn không biết ngôn ngữ, nó còn hơn thế nữa. Làm cho phương thức nạp chồng trở nên hữu ích và phù hợp hơn, như trong ví dụ trên, thực sự có thể là một tính năng tốt để thêm vào một ngôn ngữ hiện có (như đã được yêu cầu rộng rãi cho AS3), hoặc nó cũng có thể đóng vai trò là một trong nhiều trụ cột cơ bản khác nhau cho việc tạo ra một ngôn ngữ hướng thủ tục / hướng đối tượng mới.