Khi nào thì quá tải phương pháp thích hợp?


10

Giả sử tôi đang làm việc trên một hệ thống lớn, hợp lý hiện có. Tôi có một đối tượng, myObjectthuộc lớp MyClass(ví dụ: giả sử tôi đang làm việc trong Java). myObjectlà một chế phẩm có chứa a Collection, giả sử, a Listvà các đối tượng khác mà (tôi nghĩ) không liên quan. Nó chứa các phương thức ủy nhiệm chỉ dùng để gọi các phương thức của Listnó, bao gồm để đảm bảo rằng Listnó không bị lộ (xin lỗi nếu tôi hiểu sai thuật ngữ của mình).

Chúng ta hãy nói rằng đây Listlà một List<String>, nhưng vì một số lý do, phương thức truy cập chính là phương thức mặt nạ cho lớp SomeOtherClass. Nếu tôi muốn chèn một cặp giá trị mới vào Listthì tôi sẽ có một đối tượng SomeOtherClassđược gọi someObject. Tôi đã gọi myObject.insert(someObject)và bên trong insertphương thức sẽ có một số phép thuật sẽ lấy ra Stringđể đưa vào List<String>.

Giả sử bây giờ tôi chỉ có một Stringgiá trị và không có SomeOtherClassđối tượng nào để chèn. Giả sử tôi không thể sửa đổi insertphương thức vì nó sẽ phá vỡ mọi thứ trong hệ thống này. Vậy thì có nên quá tải insertphương pháp không? Hay tôi nên tạo một đối tượng mới SomeOtherClassmỗi lần tôi muốn gọi insert?

Tôi đoán rằng nếu tôi đã làm quá tải nó, nó sẽ trông giống như thế này ...

public void insert(String s) {
    ...
}

public void insert(SomeOtherObject obj) {
    this.insert(obj.magicStringMethod());
}

(Ví dụ này là một câu đố giả định dựa trên một tình huống tương tự (hơi phức tạp hơn) liên quan đến quá tải mà tôi gặp phải ngày hôm qua. Tôi sẽ mở rộng nó nếu có bất cứ điều gì không rõ ràng)

Đây sẽ là một nơi thích hợp để quá tải một phương thức? Nếu không, khi nào tôi nên quá tải một phương thức?


Giả sử Java, bạn đã xem xét việc sử dụng Generics để giải quyết vấn đề này chưa? Tôi đoán những gì tôi thực sự hỏi là String thực sự đại diện cho cái gì? Nếu nó thực sự là một đại diện của một đối tượng miền thực tế thì có một cách tiềm năng khác để giải quyết vấn đề này
Martijn Verburg

@MartijnVerburg Tôi chưa, ở giai đoạn này. Vì tôi chỉ là thực tập sinh, tôi vẫn còn hơi lạ lẫm với những thứ như mẫu thiết kế và tôi chưa có thói quen thực hành thiết kế tốt. Để trả lời cho câu hỏi thứ hai của bạn, nó là một đại diện của một đối tượng miền thực tế. magicStringMethod()trong ví dụ của tôi, trong trường hợp của tôi, có được một Stringđại diện trong SomeOtherObjectđó là một đối tượng miền.
blahman

Tôi đề nghị bạn tránh xa quá tải khi bạn có thể. Nó đôi khi gây nhầm lẫn. Tốt nhất của nó là chắc chắn về phương pháp được gọi tại thời điểm thiết kế. Xem điều này: lập trình
viên.stackexchange.com / questions / 132369 / từ

Câu trả lời:


7

Bạn quá tải khi bạn muốn hỗ trợ các loại khác nhau:

public overload void MyMethod(int value)
{ 
}

public overload void MyMethod(bool value)
{
}

public overload void MyMethod(string value)
{
}

hoặc để hỗ trợ giao diện lũy tiến sử dụng các danh sách tham số khác nhau:

public overload void MyOtherMethod()
{
    this.MyOtherMethod(DefaultValue);
}

public overload void MyOtherMethod(int value)
{
    this.MyOtherMethod(value, DefaultOtherValue);
}

public overload void MyOtherMethod(int value, bool otherValue)
{
    ...
}

Bạn thậm chí có thể hơi điên và hỗ trợ cả hai loại khác nhau VÀ giao diện tiến bộ, nhưng bạn nên nhớ rằng bạn cần tránh tạo ra các biến thể trong hành vi giữa các phương thức quá tải. Mỗi phương thức bị quá tải phải có chức năng giống như các phương thức khác trong một nhóm bị quá tải, nếu không, nó sẽ không rõ ràng về cách thức, thời điểm hoặc lý do hành vi được thay đổi. Nếu bạn muốn hai hàm làm một cái gì đó hoàn toàn khác nhau, thì bạn nên đặt tên cho chúng phù hợp.


7

Tôi muốn nói rằng quá tải là phù hợp khi cả hai phương pháp đều tương đương về mặt ngữ nghĩa. Để lấy cắp từ các ví dụ của dukeofgaming:

Đây là quá tải thích hợp:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a+b;
}

Đây không phải là:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a-b;
}

Đó là một ví dụ cực đoan (nếu bạn không bắt được nó, phương thức cuối cùng thực sự trừ thay vì thêm), nhưng ý tưởng là nếu bạn có nhiều phương thức trong một lớp có cùng tên thì chúng nên hành xử nhất quán.

Trong ví dụ của bạn, từ thông tin được cung cấp, chúng có vẻ tương đương (vì người này gọi người kia) và tôi nghĩ sẽ hợp lý khi quá tải chúng. Sẽ không hại gì khi hỏi ai đó cao cấp hơn trong nhóm của bạn nếu bạn không chắc chắn về việc có nên thêm phương thức này không nhưng nếu bạn đã thêm nó, tôi sẽ sử dụng cùng tên (nghĩa là tôi sẽ quá tải nó).


1
Cảm ơn câu trả lời. Tôi đã hỏi ai đó cao cấp hơn, nhưng vì mã ở trạng thái hơi thú vị, giải pháp của họ là một điều tôi không chắc chắn, nhưng vẫn được thực hiện (nó không phải là quá tải).
blahman

5

Về cơ bản để có các tham số của phương thức ra lệnh phương thức sẽ hành xử như thế nào.

Một ví dụ nhanh sẽ là:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    //...
}

Nếu bạn truyền tổng phương thức (a, b) một vài số nguyên, nó sẽ biết rằng nó sẽ gọi triển khai đầu tiên, vì cuộc gọi phương thức trùng với chữ ký phương thức (nghĩa là tổng hai lần (gấp đôi, gấp đôi) sẽ không hoạt động nếu bạn đưa ra phương pháp tổng hai số nguyên).

Chữ ký của cuộc gọi phải phù hợp với các triển khai có sẵn, vì vậy cố gắng gọi tổng (chuỗi, chuỗi) sẽ không hoạt động trừ khi bạn có điều này:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    public string sum(String a, String b){
        return "" + (Double.parseDouble(a) + Double.parseDouble(b));
    }
    //...
}

tl; dr: Để lớp xử lý đúng phương thức theo bất kỳ tham số nào bạn đưa ra một phương thức có cùng tên.

Khi kế thừa, nó được gọi là ghi đè

Một ví dụ điển hình của kịch bản khác này là khi bạn muốn thay đổi hành vi mặc định của một lớp bằng cách kế thừa nó, và đặc biệt khi bạn có một cái gì đó tương tự như mẫu phương thức mẫu, trong đó bạn có từng bước của một thuật toán được triển khai trong một phương thức.

Hãy tưởng tượng rằng bạn có class Robotvà một phương thức gọi là fireAtTarget(Target target)... gọi fireWeaponA(target); fireWeaponB(target); fireWeaponC(target);nhau. Bạn cũng muốn có một bộ sưu tập có tên robot_army mà bạn chỉ có thể thêm các đối tượng của lớp Robot. Robot bắn súng máy theo mặc định trong các fireWeaponX()phương pháp của nó .

Sau đó, bạn muốn có class LazorRobotclass MissileRobot, để bạn thực hiện Robot khắp nơi trên một lần nữa ?, không, chỉ cần có LazorRobot và MissileRobot kế thừa Robot, và quá tải mỗi fireWeaponX()phương pháp để lazorz sử dụng hoặc tên lửa và bạn sẽ có hành vi tương tự với các loại vũ khí khác nhau, mà không cần phải reimplement phần còn lại của các phương pháp.

tl; dr: Để có hành vi của phương thức phụ thuộc vào lớp mà không phá vỡ giao diện (robot_army chỉ chấp nhận Robot, nhưng chấp nhận mở rộng bất kỳ lớp nào kế thừa Robot).


Ví dụ đầu tiên của bạn là ghi đè . Khi bạn duy trì giao diện của phương thức nhưng thay đổi hành vi trong hậu duệ. Hàm ý là bạn không có ý định đưa ra một phương pháp thay thế. Ví dụ thứ hai của bạn là quá tải chính xác. Nếu ý định của bạn là làm nổi bật khi nào ghi đè so với khi quá tải, bạn có thể muốn làm cho điều này rõ ràng hơn trong câu trả lời của bạn, nếu không thì toàn bộ when inheritingphần có lẽ là không cần thiết. :)
S.Robins

Derp, caffeine đã giúp tôi lần này = P, để lại phần thứ hai là phần thứ nhất và phần thứ nhất là phần thứ hai vì lợi ích chung. Cảm ơn vì sự đúng đắn của bạn.
dukeofgaming

Nếu tôi hiểu chính xác câu trả lời của bạn, tôi chỉ nên quá tải khi nhìn vào các tham số sẽ cho phép tôi suy ra sự khác biệt trong hành vi giữa, nói sum(String, String)sum(int, int)?
blahman

@blahman bạn nên quá tải khi bạn muốn sử dụng các loại khác nhau, hỗn hợp các loại hoặc thậm chí để thêm các tham số bổ sung. Quá tải cung cấp một cách để tạo các phương thức với các tham số mặc định trong đó cú pháp mặc định param = value không được hỗ trợ. Xem câu trả lời của tôi để xem những gì tôi có ý nghĩa.
S.Robins

1
@blahman BTW và trên một chủ đề khác, khi bạn có quá nhiều tham số và một số trong số chúng là tùy chọn đôi khi tốt hơn là chỉ gửi một đối tượng hoặc cấu trúc dữ liệu có tất cả các giá trị mặc định, vì vậy bạn thay đổi các thuộc tính của đối tượng hoặc dữ liệu đó kết cấu. Bằng cách đó, bạn không cần tạo 20 phiên bản của cùng một phương thức chỉ để thêm một tham số mỗi lần.
dukeofgaming
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.