メソッドのオーバーロードはいつ適切ですか?


10

既存のかなり大きなシステムで作業しているとしましょう。myObjectクラスのオブジェクトがありますMyClass(例として、Javaで作業しているとします)。myObjectCollection、たとえばa Listと、(私が思うに)無関係な他のオブジェクトを含むコンポジションです。これには、Listそれが構成されているメソッドを呼び出す役割を果たしているデリゲートメソッドが含まれていますList

これListがaであるとしましょう。List<String>何らかの理由で、メインのアクセスメソッドはclassのマスクメソッドですSomeOtherClass。新しい値のペアをmyに挿入しListたい場合は、SomeOtherClass呼び出されたオブジェクトがありますsomeObject。私が呼び出すmyObject.insert(someObject)と、insertメソッド内にを取得してStringに入れる魔法がいくつかありますList<String>

今、String値しか持っておらずSomeOtherClass、挿入するオブジェクトがないとします。insertこのシステムのすべてを破壊するため、メソッドを変更できないと仮定します。次に、insertメソッドをオーバーロードする必要がありますか?またはSomeOtherClass呼び出すたびに新しいオブジェクトを作成する必要がありますinsertか?

オーバーロードするとこんな感じになりますね…

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

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

(この例は、昨日発生したオーバーロードに関する同様の(やや複雑な)状況に基づいた不自然なパズルです。不明な点がある場合は展開します)

これはメソッドをオーバーロードするのに適切な場所でしょうか?そうでない場合、いつメソッドをオーバーロードする必要がありますか?


Javaを想定して、この問題を解決するためにジェネリックスを使用することを検討しましたか?私が本当に求めているのは、文字列が実際に何を表しているのでしょうか?それは本当に実際に実際のドメインオブジェクトの表現だ場合は、この問題を解決するための別の潜在的な方法があります
マルタインVerburg

@MartijnVerburgまだこの段階ではありません。私はただのインターンなので、デザインパターンなどにはまだ慣れていません。デザインの習慣はまだありません。2番目の質問への回答では、これは実際のドメインオブジェクトの表現です。magicStringMethod()私の例では、私の場合、どちらがドメインオブジェクトStringSomeOtherObjectあるかの表現を取得します。
blahman

可能な場合は、過負荷を避けてください。それは時々混乱を引き起こします。設計時に呼び出されたメソッドを確認するのが最善です。:この参照programmers.stackexchange.com/questions/132369/...
NoChance

回答:


7

さまざまなタイプをサポートする場合は、オーバーロードします。

public overload void MyMethod(int value)
{ 
}

public overload void MyMethod(bool value)
{
}

public overload void MyMethod(string value)
{
}

または、さまざまなパラメーターリストを使用してプログレッシブインターフェイスをサポートするには:

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)
{
    ...
}

少し頭がおかしくなり、異なるタイプとプログレッシブインターフェイスの両方をサポートすることもありますが、オーバーロードされたメソッド間で動作のバリエーションを作成しないようにする必要があることに注意してください。オーバーロードされた各メソッドは、オーバーロードされたグループ内の他のメソッドと機能的に同じである必要があります。そうでない場合、動作がどのように、いつ、なぜ変更されるのかが明確になりません。2つの関数でまったく異なる処理を実行する場合は、それに応じた名前を付ける必要があります。


7

両方のメソッドが意味的に同等である場合、オーバーロードが適切であると私は言うでしょう。dukeofgamingの例から盗むには:

これらは適切にオーバーロードされています:

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

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

これらは:

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

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

これは極端な例です(キャッチできなかった場合、最後のメソッドは実際に加算ではなく減算します)が、同じ名前のクラスに複数のメソッドがある場合、それらは一貫して動作するはずであるという考え方です。

あなたの例では、与えられた情報から、それらは等価であるように見え(一方が他方を呼び出すため)、それらをオーバーロードすることは合理的だと思います。メソッドを追加する必要があるかどうかわからない場合でも、チームの上級者に聞いても問題はありませんが、追加した場合は同じ名前を使用します(つまり、オーバーロードします)。


1
答えてくれてありがとう。私はもっ​​と年長の人に尋ねましたが、コードはやや興味深い状態にあるため、それらの解決策は私にはわかりませんでしたが、それでも実装されました(過負荷ではありませんでした)。
blahman

5

基本的に、メソッドのパラメーターがメソッドの動作を決定するようにします。

簡単な例は次のとおりです。

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

メソッドsum(a、b)に2つの整数を渡すと、最初の実装を呼び出す必要があることがわかります。これは、メソッド呼び出しがメソッドシグネチャと一致するためです(つまり、次のように指定した場合、double sum(double、double)は機能しません。合計法2つの整数)。

呼び出しのシグネチャは利用可能な実装と一致している必要があるため、sum(string、string)を呼び出そうとしても、これがない限り機能しません。

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:同じ名前のメソッドに与えるパラメーターに応じて、クラスが正しいメソッドを処理するようにします。

継承する場合は上書きと呼ばれます

この他のシナリオの良い例は、クラスを継承することによってクラスのデフォルトの動作を変更する場合、特に、メソッドに実装されているアルゴリズムの各ステップがあるテンプレートメソッドパターンに似たものがある場合です。

あなたが持っていclass Robotて、呼び出されるメソッドfireAtTarget(Target target)fireWeaponA(target); fireWeaponB(target); fireWeaponC(target);次々と呼び出されると想像してください。Robotクラスのオブジェクトのみを追加できるrobot_armyというコレクションも必要です。ロボットは、fireWeaponX()メソッドのデフォルトで機関銃を発射します。

次に、あなたがしたいclass LazorRobotclass MissileRobot、あなたはすべての繰り返しロボットを実装しない?いいえ、ちょうどLazorRobotを持っているとMissileRobotはロボットを継承し、過負荷にそれぞれfireWeaponX()使用lazorzやミサイルへの方法を、あなたが再実装することなく、さまざまな武器と同じ動作になります残りのメソッド。

tl; dr:インターフェイスを壊すことなく、メソッドの動作をクラスに依存させるには(robot_armyはRobotのみを受け入れますが、Robotを継承するすべてのクラスを受け入れます)。


最初の例はオーバーライドです。メソッドのインターフェースを維持しながら、子孫の動作を変更する場合。つまり、代替方法を提供するつもりはないということです。ただし、2番目の例は正しくオーバーロードしています。オーバーライドするタイミングとオーバーロードするタイミングを強調する場合は、回答でこれをより明確にすることwhen inheritingができます。それ以外の場合、セクション全体はおそらく必要ありません。:)
S.Robins

Derp、カフェインは今度は私を取得しました= P、一般的な関心のために、2番目の部分を最初の部分として、最初の部分を2番目の部分として残しました。訂正ありがとうございます。
dukeofgaming

私があなたの答えを正しく理解している場合、パラメーターを見ると、たとえばとの動作の違いを推測できる場合にのみ、オーバーロードする必要がsum(String, String)ありsum(int, int)ます。
blahman

@blahmanは、異なるタイプ、タイプの混合を使用する場合、または追加のパラメーターを追加する場合でもオーバーロードする必要があります。オーバーロードは、param = valueのデフォルト構文がサポートされていないデフォルトパラメータを使用してメソッドを作成する方法を提供します。私の意味を確認するには、私の答えを参照してください。
S.Robins

1
@blahman BTWおよび別のテーマでは、パラメータが多すぎて一部がオプションである場合、すべてのデフォルト値を持つ単一のオブジェクトまたはデータ構造を送信する方がよい場合があるため、そのようなオブジェクトまたはデータの属性を変更します。構造。そうすれば、毎回1つのパラメーターを追加するだけで、同じメソッドの20バージョンを作成する必要がなくなります。
dukeofgaming
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.