2

私はcompareCriteriaを比較しようとしています。'between'と'inArray'または'greaterThan'のような単純なもの。これらのクラスにはポリモーフィズムを使用します。compareCriteriaインターフェースから共有する1つのメソッドは、「matchCompareCriteria」です。

私が避けようとしているのは、各クラスに、照合する必要のあるcompareCriteriaのタイプをチェックさせることです。たとえば、inArrayオブジェクトは、matchCompareCriteriaがinArrayオブジェクトに渡されたかどうかをチェックします。渡されなかった場合は、比較方法がわかっている場合はfalseを返します。

この場合、instanceofは完全に正当である可能性があります(オブジェクトは自分自身について知っています)が、それでも私はそれを回避するための可能な方法を探しています。何か案は?

擬似コードの例:

betweenXandY = create new between class(x, y)
greaterThanZ = create new greaterThan class(z)
greaterThanZ.matchCompareCriteria(betweenXandY)

XとYがZより大きい場合、trueを返します。

編集:

1)instanceofは、今のところ、matchCompareCriteriaメソッドで必要に応じて表示されるものです。私はそれを取り除きたいです

2)matchCompareCriteraは、compareCriteriaが別のCriteriaに含まれているかどうかを確認します。一方の可能なすべての値がもう一方に含まれている場合、trueを返します。compareCriteriaの多くの組み合わせでは、それらを比較しても意味がないため、falseが返されます(betweenAlfaとbetweenNumは互換性がありません)。

4

5 に答える 5

8

あなたが説明している問題は、ダブルディスパッチと呼ばれています。この名前は、2つのオブジェクトのタイプ(したがって、double)に基づいて、実行(ディスパッチ)するコードのビットを決定する必要があるという事実に由来しています。

通常、OOには単一のディスパッチがあります。オブジェクトに対してメソッドを呼び出すと、そのオブジェクトのメソッドの実装が実行されます。

あなたの場合、2つのオブジェクトがあり、実行される実装は両方のオブジェクトのタイプによって異なります。基本的に、これまでは標準的なOOの状況のみを扱っていた場合に「気分が悪い」という、これによって暗示される結合があります。しかし、それは実際には間違っていません。OOの基本機能が直接解決に適しているという問題領域から少し外れています。

動的言語(またはこの目的のために十分に動的であるリフレクション付きの静的型付き言語)を使用している場合は、基本クラスのディスパッチャーメソッドを使用してこれを実装できます。擬似コードの場合:

class OperatorBase
{
    bool matchCompareCriteria(var other)
    {
        var comparisonMethod = this.GetMethod("matchCompareCriteria" + other.TypeName);
        if (comparisonMethod == null)
            return false;

        return comparisonMethod(other);
    }
}

ここでは、この言語には、呼び出されるすべてのクラスに組み込みのメソッドがGetMethodあり、名前でメソッドを検索できることと、オブジェクトのタイプの名前を取得するすべてのオブジェクトのTypeNameプロパティがあることを想像しています。したがって、他のクラスがGreaterThanであり、派生クラスにmatchCompareCriteriaGreaterThanというメソッドがある場合、そのメソッドを呼び出します。

class SomeOperator : Base
{
    bool matchCompareCriteriaGreaterThan(var other)
    {
        // 'other' is definitely a GreaterThan, no need to check
    }
}

したがって、正しい名前でメソッドを作成するだけで、ディスパッチが発生します。

引数の型によるメソッドのオーバーロードをサポートする静的に型付けされた言語では、連結された命名規則を発明する必要がなくなります。たとえば、ここではC#です。

class OperatorBase
{
    public bool CompareWith(object other)
    {
        var compare = GetType().GetMethod("CompareWithType", new[] { other.GetType() });
        if (compare == null)
            return false;

        return (bool)compare.Invoke(this, new[] { other });
    }
}

class GreaterThan : OperatorBase { }
class LessThan : OperatorBase { }

class WithinRange : OperatorBase
{
    // Just write whatever versions of CompareWithType you need.

    public bool CompareWithType(GreaterThan gt)
    {
        return true;
    }

    public bool CompareWithType(LessThan gt)
    {
        return true;
    }
}

class Program
{
    static void Main(string[] args)
    {
        GreaterThan gt = new GreaterThan();
        WithinRange wr = new WithinRange();

        Console.WriteLine(wr.CompareWith(gt));
    }
}

モデルに新しいタイプを追加する場合は、以前のすべてのタイプを調べて、何らかの方法で新しいタイプと対話する必要があるかどうかを自問する必要があります。したがって、すべての型は、他のすべての型と相互作用する方法を定義する必要があります-相互作用が本当に単純なデフォルトである場合でも(「return以外は何もしないtrue」など)。その単純なデフォルトでさえ、あなたがしなければならない意図的な選択を表しています。これは、最も一般的なケースでコードを明示的に記述する必要がないという便利さによって偽装されています。

したがって、すべてのオブジェクトに分散させるのではなく、外部テーブル内のすべてのタイプ間の関係をキャプチャする方が理にかなっている場合があります。一元化することの価値は、タイプ間の重要な相互作用を見逃したかどうかを即座に確認できることです。

したがって、タイプを別の辞書にマップする辞書/マップ/ハッシュテーブル(言語で呼び出されるものは何でも)を持つことができます。2番目のディクショナリは、2番目のタイプをこれら2つのタイプの正しい比較関数にマップします。一般的なCompareWith関数は、そのデータ構造を使用して、呼び出す適切な比較関数を検索します。

どちらのアプローチが正しいかは、モデルに含まれる可能性のあるタイプの数によって異なります。

于 2009-09-12T23:05:40.030 に答える
5

参照instanceofしているので、ここではJavaで作業していると思います。これにより、オーバーロードを利用できる場合があります。と呼ばれるインターフェースを考えてみましょう。このインターフェースSomeInterfaceには、次の1つのメソッドがあります。

public interface SomeInterface {
    public boolean test (SomeInterface s);
}

ここで、SomeInterfaceSome1とを実装する2つの(巧妙な名前の)クラスを定義しますSome2Some2退屈です:test常にfalseを返します。ただし、Some1は、 :testを指定すると関数をオーバーライドします。Some2

public class Some1 implements SomeInterface {
    public boolean test (SomeInterface s) {
        return false;
    }

    public boolean test (Some2 s) {
        return true;
    }
}

これにより、タイプチェックを行うifステートメントの行が次々と表示されるのを防ぐことができます。ただし、注意点があります。このコードを考えてみましょう:

Some1 s1 = new Some1 ();
Some2 s2 = new Some2 ();
SomeInterface inter = new Some2 ();

System.out.println(s1.test(s2));     // true
System.out.println(s2.test(s1));     // false
System.out.println(s1.test(inter));  // false

その3番目のテストを見ますか?interタイプはですが、代わりにSome2扱われSomeInterfaceます。過負荷の解決は、Javaのコンパイル時に決定されるため、完全に役に立たなくなる可能性があります。

それはあなたを正方に戻します:using instanceof(これは実行時に評価されます)。あなたがそのようにそれをしたとしても、それはまだ悪いデザインです。各クラスは、他のすべてについて知っている必要があります。別のものを追加することにした場合は、既存のすべてに戻って、新しいクラスを処理する機能を追加する必要があります。これは急いでひどく維持できなくなります。これは設計が悪いことの良い兆候です。

再設計が必要ですが、より多くの情報がなければ、私はあなたに正しい方向への特に良いプッシュを与えることができません。

于 2009-09-10T19:21:51.183 に答える
1

Criteriaと呼ばれるスーパークラスまたはインターフェースを作成する必要があります。次に、各具象サブクラスがCriteriaインターフェースを実装します。間、より大きいなどが基準です。

Criteriaクラスは、Criteriaを受け入れるmatchCompareCriteriaメソッドを指定します。実際のロジックはサブクラスにあります。

ストラテジーデザインパターンまたはテンプレートデザインパターンのいずれかを探しています。

于 2009-09-10T18:21:44.527 に答える
1

私がよく理解しているなら、あなたの方法は型チェックに依存しています。それを避けるのは非常に難しく、ポリモーフィズムは問題の解決に失敗します。あなたの例から、メソッドの振る舞いはこれに依存しているので、inArrayはパラメータのタイプをチェックする必要があります。ポリモーフィズムではそれを行うことはできません。つまり、このケースを処理するためにクラスにポリモーフィックメソッドを配置することはできません。これは、matchCompareCriteriaが、パラメーターの動作ではなく、パラメーターのタイプに依存しているためです。

instanceofオブジェクトのタイプをチェックして、どのような動作をするかを選択する場合、使用しないというルールが有効です。明らかに、その動作は、タイプをチェックするさまざまなオブジェクトに属します。ただし、この場合、オブジェクトの動作は渡されたオブジェクトのタイプに依存し、以前のように呼び出されたオブジェクトではなく、呼び出し元のオブジェクトに属します。ケースは、をオーバーライドする場合と似ていますequals()。渡されたオブジェクトがオブジェクトと同じタイプになるように型チェックをthis実行してから、動作を実装します。テストが失敗した場合は、falseを返します。それ以外の場合は、同等性テストを実行します。

結論:instanceofこの場合、使用は問題ありません。

これはSteveYeggeからのより長い記事であり、単純でわかりやすい例を使用して、より適切に説明していると思います。これはあなたの問題にぴったりだと思います。

覚えておいてください:そうでない場合を除いて、ポリモーフィズムは良いです。:)

于 2009-09-12T22:34:41.873 に答える
1

Smalltalkのアプローチは、階層により多くのレイヤーを導入することです。したがって、betweengreaterThanrangedCompareCriteria(または何か)のサブクラスであり、rangeCompareCriteria :: matchCompareCriteriaは、それ自体の2つのインスタンスが比較可能かどうかを尋ねられたときにtrueを返します。

そういえば、「matchCompareCriteria」の名前を、意図をもう少しよく表現する名前に変更することをお勧めします。

于 2009-09-12T23:07:18.857 に答える