2

以下の単純化された例のように、包含オブジェクトを引数として包含オブジェクトのメソッドに渡すのは悪い設計ですか?

class A
{
    private B _containedObject;

    public A(B b)
    {
            _containedObject = b;
            //...
    }

    public void SomeMethod()
    {
            //...
            _containedObject.SomeMethod(this);
            //...
    }
}

class B
{
    public void SomeMethod(A a)
    {
            //do something with a
    }
}

Ps 上記の例は、包含関係と、包含オブジェクトを包含オブジェクトに渡すことのみを示すために単純化されており、それ自体はそうする目的を示していません。目的がありますのでご安心ください。

4

6 に答える 6

2

この単純な例では、ダブル ディスパッチの形式のように見えます。この場合、A を B のメソッド引数として渡します。

void SomeMethod(A a)
{
    a.DoSomething(this);
}

ただし、これはほんの一例です。オブジェクトを ctor 引数として渡す方が有益な場合もあります。

于 2012-01-03T16:21:45.887 に答える
1

包含オブジェクトをパラメーターとして包含オブジェクトに渡したい場合があります。他の投稿者が指摘しているように、それは「二重発送」と呼ばれています。Smalltalk 言語は典型的な例です。

この例では、整数と浮動小数点数 (または他の種類の数値) を加算します。C++ または Java では、整数クラスに次のようなポリモーフィック メソッドがあります。

Integer {
     add(Float) {...}
     add(Integer) {...}
     add(UserDefinedType) {...}
}

各 add メソッドは、さまざまな種類の正しい加算を実装できます。コンパイラは、型情報を使用して正しいメソッドを選択します。ステートメント Integer(6).add(Float(1.0)) は Integer::add(Float) メソッドを呼び出します。

C++ や Java とは異なり、Smalltalk は動的に型付けされます。Smalltalk クラスはaddという名前のメソッドを 1 つだけ持つことができます。

Integer::add(オブジェクト)

コンパイラは「オブジェクト」のクラスを知りません。そのため、Smalltalk はC++ や Java と同じ方法でadd を実装することはできません。代わりに、Smalltalk は二重ディスパッチを使用します。それはこのように動作します:

   Integer {
          add(Object o) {
              return o.addToInteger(this)
          }        
    }

Float、Fraction、さらには Integer などの他の数値クラスは、メソッド addToInteger() を実装しています。

Float {
      addToInteger(Object o) {
          //Perform float + in math
          return result
     }
}

二重ディスパッチは、動的に型付けされた言語で man 操作をポリモーフィックに実装する方法の問題を解決します。しかし、もっと一般的な使い方があります: 訪問者パターンです。それは別の回答に入れます。

于 2012-01-04T05:33:41.907 に答える
1

おそらく、この手法を使用する最も一般的な理由は、訪問者パターンです。Web で気に入った例を見つけるのに苦労しているので、ウィキペディアの記事からクラス図を使用します。この図を見てください: http://upload.wikimedia.org/wikipedia/commons/5/59/VisitorPatternUML.png

次に、ビジター オブジェクトを受け入れる自動車部品をイメージします。

Engine myEngine;
CarElementPrintVistor myVisitor;
myEngine.accept(myVisitor);

しかし、accept() メソッド内では、車の部分が反転して訪問者に渡されます!

myVisitor.visit(this)

詳細については、ビジター デザイン パターンを確認してください。

于 2012-01-04T05:50:42.053 に答える
1

オブジェクトの親が存在し、オブジェクトの構築時に既知であり、オブジェクトが他の親を持つことがない場合、構築時に各子オブジェクトに親への参照を保持させると便利なことがよくあります。子オブジェクトは、その親が誰であるかを既に知っているため、親を引数として渡さなくても、子オブジェクトにその親が関与する何かを実行するように要求できます。

この概念の問題点の 1 つは、クラスの機能によっては、親タイプのオブジェクトを親としてオブジェクトを作成できるユーザーを制限できるようにする場合があることです。internal親と子の型が同じアセンブリにある場合、これは子のコンストラクターの修飾子で簡単に行うことができます。親オブジェクトがまだ書かれていないアセンブリで定義されたクラスである可能性を許可したいが、そのような親オブジェクトに、「同意」なしに「アタッチ」されないというコンパイル時の保証を提供したい場合は、次のようになります。少しトリッキーですが、プライベート コンストラクターとパブリック ファクトリ メソッドを使用することで、おそらくかなりうまくいくでしょう。

パブリック インターフェイス IAssuredParent<T>
static childClass createAttachedChild<T,U>(T parent) where T:IAssuredParent<U>
{...};

親がPプライベート クラスXYZを定義して を実装するIAssuredParent<XYZ>場合、それは type にアクセスできる唯一のクラスになり、したがって、型の実パラメーターに対応でき、型の制約に準拠XYZできる型パラメーターのペアで createAttachedChild を正常に呼び出すことができる唯一のクラスになります。 P.

于 2012-01-03T17:08:20.480 に答える
1

いいえ、これは悪い設計ではなく、かなり頻繁に使用されます。ただし、これを行うとメモリ リークが発生しないように注意する必要があります。コンテナ オブジェクトを内部オブジェクトに渡すと、内部オブジェクトが削除されるまで親への参照が保持されます。

つまり、A を新たに作成し、それが ab を更新した場合、他のすべての参照を期限切れにしても、B がコピーを保持しているため、A は存在し続けます。

于 2012-01-03T16:19:19.560 に答える