Chúng ta có nên đổi tên các phương thức quá tải?


14

Giả sử một giao diện chứa các phương thức này:

Car find(long id);

List<Car> find(String model);

Có phải tốt hơn để đổi tên họ như thế này?

Car findById(long id);

List findByModel(String model);

Thật vậy, bất kỳ nhà phát triển nào sử dụng API này sẽ không cần nhìn vào giao diện để biết các đối số có thể có của các find()phương thức ban đầu .

Vì vậy, câu hỏi của tôi là tổng quát hơn: lợi ích của việc sử dụng các phương thức quá tải trong mã là gì vì nó làm giảm khả năng đọc?


4
Hoặc là phương pháp được chấp nhận miễn là bạn nhất quán.
ChrisF

Có một mối quan hệ giữa quá tải và ghi đè một phương thức. Tuy nhiên bài viết này ủng hộ đề xuất của bạn - Nó có thể được quan tâm: roseindia.net/javatutorials/
mẹo

Câu trả lời:


23

Đây là một vấn đề tương đối nhỏ so với nhiều thực tiễn dễ đọc khác mà bạn có thể dễ mắc phải, vì vậy tôi nói rằng vấn đề chính là cách bạn đặt tên cho phương pháp của mình.

Như đã nói, nếu bạn định làm gì đó với nó, tôi sẽ làm theo cách này:

  • Quá tải nếu ...

    Các phương thức tuân theo gần như cùng một hợp đồng nhưng chỉ hoạt động trên các đầu vào khác nhau (hãy tưởng tượng một nhà điều hành điện thoại có thể tra cứu tài khoản của bạn bằng số ID thuế cá nhân, số tài khoản của bạn hoặc tên và ngày sinh của bạn). Điều này bao gồm trả về cùng loại đầu ra .

  • Sử dụng tên khác nếu ...

    Các phương thức thực hiện những điều khác nhau đáng kể hoặc trả lại đầu ra khác nhau (như trường hợp của bạn). Bạn có thể cân nhắc sử dụng một tên khác nếu một người truy cập cơ sở dữ liệu và một tên thì không.

    Ngoài ra, nếu loại trả về là khác nhau, tôi cũng sẽ thay đổi động từ để chỉ ra rằng:

    Car findById(long id);
    
    List findAllByModel(String model);
    

3
FWIW, tôi sẽ nói một cách mạnh mẽ hơn một chút: "Các phương thức tuân theo chính xác cùng một hợp đồng ..." Tức là kiểu / số đối số không quan trọng - ngữ nghĩa của lệnh gọi hàm là giống hệt nhau. Nếu loại đối số / số lượng vấn đề, thì bạn không nên quá tải.
mcmcc

4

Tôi sẽ khuyên bạn nên sử dụng một tên khác nhau, trong mọi trường hợp. Có thể vào một lúc nào đó trong tương lai, bạn sẽ muốn thêm một phương pháp khác List<Car> findByMake(String make), ngược lại List<Car> findByModel(String model). Vì vậy, đột nhiên, gọi tất cả mọi thứ finddừng lại có ý nghĩa. Phương pháp của bạn cũng ít có khả năng vô tình được sử dụng không chính xác, nếu tên của họ cung cấp thêm thông tin về cách sử dụng chúng.


1
Công bằng mà nói, điều này sẽ không nhiều như một vấn đề nếu chức năng được thể hiện rõ ràng hơn bởi các đối tượng: find(Make val)find(Model val). Sau đó, các phương pháp tiện lợi như findByMake(String val)sẽ rõ ràng hơn nhiều những gì họ đang thực sự làm. Rốt cuộc, một Stringkhông phải là một kiểu dáng hay một mô hình, vì vậy phương pháp phải giải thích những gì nó thực sự đang làm.
Nicole

4

Nếu bạn đổi tên một phương thức, nó sẽ không còn bị quá tải nữa. Nói chung, quá tải không nhất thiết làm cho mã ít đọc hơn, tuy nhiên nó có thể làm cho việc thực hiện khó thực hiện hơn nếu cú ​​pháp không rõ ràng.

Nhiều ngôn ngữ sử dụng nạp chồng phương thức làm phương tiện để hiển thị giao diện cho chức năng trong đó các tham số có thể là tùy chọn và mặc định cho các tham số tùy chọn được ngụ ý. Điều này đặc biệt đúng đối với các ngôn ngữ không hỗ trợ cú pháp tham số mặc định trong khai báo phương thức.

Vì vậy, làm điều này:

void MyMethod(int param1, int param2 = 10)
{
    ...
}

cứu bạn khỏi việc này:

void MyMethod(int param1)
{
    MyMethod(param1, Param2Default);
}

void MyMethod(int param1, int param2)
{
    ....
}

Như dễ đọc hơn, điều đó thực sự đến với bạn. Cá nhân tôi thích tùy chọn thứ hai, đặc biệt khi danh sách tham số hơi dài, nhưng tôi cho rằng nó không thực sự quan trọng miễn là bạn nhất quán trong suốt API của mình.

Khó khăn với quá tải xuất hiện khi bạn muốn các hàm về cơ bản giống nhau và bạn muốn các danh sách tham số giống nhau, nhưng các kiểu trả về sẽ khác nhau. Hầu hết các ngôn ngữ không biết cách phân biệt giữa hai phương thức được đặt tên giống nhau, nhưng với các kiểu trả về khác nhau. Tại thời điểm này, bạn cần suy nghĩ về việc sử dụng generic, thay đổi giao diện tham số hoặc đổi tên một trong các phương thức của bạn để chỉ ra sự khác biệt trong loại trả về. Đây là nơi dễ đọc có thể trở thành một vấn đề lớn, nếu bạn không giải quyết một kế hoạch đặt tên đơn giản và rõ ràng để giải quyết các tình huống như thế này.

Đặt tên cho các phương thức quá tải của bạn GetSomething()GetSomethingEx()sẽ không nói nhiều về sự khác biệt giữa các phương thức của bạn, đặc biệt nếu đó là các kiểu trả về là sự khác biệt duy nhất giữa chúng. Mặt khác, GetSomethingAsInt()GetSomethingAsString() cho bạn biết thêm một chút về những gì các phương thức đang làm, và trong khi không hoàn toàn quá tải, hãy chỉ ra rằng hai phương thức này làm những việc tương tự nhau, nhưng trả về các loại giá trị khác nhau. Tôi biết rằng có nhiều cách khác bạn có thể đặt tên cho các phương thức, tuy nhiên với mục đích minh họa điểm, những ví dụ thô thiển này nên làm.

Trong ví dụ về OP, việc đổi tên không thực sự cần thiết vì các tham số của phương thức là khác nhau, tuy nhiên, điều này làm cho mọi thứ rõ ràng hơn một chút để đặt tên cho một phương thức cụ thể hơn. Cuối cùng, nó thực sự thuộc về loại giao diện bạn muốn trình bày cho người dùng của mình. Một quyết định về việc không quá tải không nên được đưa ra chỉ dựa trên nhận thức của bạn về khả năng đọc. Ví dụ, các phương thức quá tải có thể đơn giản hóa giao diện API và giảm số lượng phương thức mà nhà phát triển có thể cần nhớ, mặt khác, nó có thể làm mờ giao diện đến một mức độ sau đó yêu cầu nhà phát triển đọc tài liệu phương thức để hiểu mẫu nào về phương thức sử dụng, trong khi có một số phương thức được đặt tên mô tả tương tự có thể làm cho nó rõ ràng hơn chỉ cần đọc một tên phương thức theo mục đích của nó.


0

Ưu tiên quá tải miễn là các phương thức trả lại cùng một điều và theo cùng một hợp đồng. Quá tải giải phóng mã cuộc gọi khỏi cam kết không cần thiết đến loại tham số.

Giả sử chức năng gọi nhận được một truy vấn tìm kiếm dưới dạng tham số và thực hiện một số xử lý khác trước và / hoặc sau khi gọi đến find.

void tryToSellCars(String which) {
    /* grab an airhorn, inflatable tube guy... */
    List<Car> cars = find(which);
    /* expound virtues of each car in detail... */
}

Nếu bạn muốn thay đổi loại truy vấn đó vì bất kỳ lý do gì (ví dụ: từ một chuỗi ID đơn giản thành một đối tượng truy vấn có tính năng đầy đủ của một số loại), bạn có thể thực hiện thay đổi đó trong chức năng gọi chỉ bằng cách thay đổi chữ ký hàm để chấp nhận loại tham số mới mà không phải lo lắng về việc thay đổi phương thức mà nó gọi trên lớp của bạn.

void tryToSellCar(CarQuery which) {
    /* grab airhorn, inflate tube guy... */
    List<Car> cars = find(which)
    /* expound virtues of each car in detail... */
}

Nếu bạn thực hiện findByIdfindByQueryObjectriêng rẽ, bạn sẽ phải tìm kiếm mọi cuộc gọi để thực hiện thay đổi đó. Trong ví dụ này, tôi chỉ thay đổi một từ và tôi đã hoàn thành.


Tất nhiên, câu trả lời này giả định rằng bạn đang sử dụng một ngôn ngữ hỗ trợ quá tải với các lỗi thời gian biên dịch cho một tham số không hợp lệ. Nếu bạn đang viết JavaScript hoặc Ruby hoặc bất kỳ ngôn ngữ nào khác không hỗ trợ quá tải, tôi sẽ luôn sử dụng verbose findByFoođể bắt các kiểu không khớp trước đó.
sqykly
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.