12

クラスのデータメンバーからの入力を引数として取るFOO()クラスのメソッドがあります(2つのfloatと1つのintであるとしましょう)。私がこれを理解している方法では、一般的に次のような方法でこれを実装することをお勧めします。AB

A->FOO1(B, other_data_x)

それよりも

A->FOO2(B.member1, B.member2, B.member3, other_data_x).

唯一ではありませんが、これの利点は、どのメンバーBにアクセスするかの詳細が残されFOO1()ているため、実装の詳細を隠すのに役立つということです。

しかし、私が疑問に思っているのは、これが実際にクラスAB. A前者の場合のクラスは、クラスBが存在することを( のようなものを介してinclude class_B_header.h)知る必要があり、メンバーがB変更されたり、別のクラスに移動されたり、クラスが完全に削除されたりした場合は、それに応じBて変更する必要があります。対照的に、後者のアプローチでは、 class が存在するかどうかは気にしません。実際、気にするのは、2 つの float (この場合はandで構成されます) と int ( ) が提供されることだけです。確かに、後者の例にもカップリングがありますが、このカップリングはどこでも処理されますAFOO1()FOO2()BB.member1B.member2B.member3FOO2()またはFOO2()の定義ではなく、たまたま呼び出しているクラスが呼び出されます。AB

この質問の2番目の部分は、次のようなソリューションを実装したい場合にA、分離する良い方法はありますか?BFOO1()

4

8 に答える 8

18

しかし、私が疑問に思っているのは、これが実際にクラス A と B の間に追加の結合を導入するかどうかということです。

はい、そうです。 が密結合されていますAB

オブジェクトのメンバーではなくオブジェクトを渡す必要があることが一般的に受け入れられているという印象を受けているようです。どのようにこのような印象を受けたかはわかりませんが、そうではありません。オブジェクトまたはそのオブジェクトのメンバーを送信する必要があるかどうかは、何をしようとしているのかに完全に依存します。

2 つのエンティティ間に密接に結合された依存関係を持つことが必要かつ望ましい場合もあれば、そうでない場合もあります。ここに適用される一般的な経験則がある場合、それはあなたが提案したことの反対であると言えます。

可能な限り依存関係を排除しますが、それ以外の場所では排除しません。

于 2013-05-23T14:26:57.330 に答える
5

エンジニアリングの世界へようこそ。ここではすべてが妥協であり、適切な解決策は、クラスと関数がどのように使用されることになっているか (およびその意味が想定されているか) によって異なります。

概念的foo()に、結果がa 、 an 、および aに依存するものである場合、 a 、an 、およびaを受け入れるのは正しいことです。これらの値がクラスのメンバーから取得されるかどうか ( である可能性がありますが、またはである可能性もあります) は重要ではありません。floatintstringfloatintstringBCDfoo()B

一方、 iffoo()概念的に、結果が状態に依存するものである場合 (Bたとえば、その状態の抽象化を実現するため)、 type のオブジェクトを受け入れるようにしますB

また、適切なプログラミング手法は、可能であれば関数が少数の引数を受け入れるようにすることです。誇張せずに 3 つまでと言っていいでしょう。そのため、関数が複数の値で論理的に機能する場合は、それらの値をグループ化することをお勧めします。をデータ構造に格納し、そのデータ構造のインスタンスを に渡しますfoo()

そのデータ構造を と呼ぶとB、元の問題に戻りますが、セマンティックな違いがあります!

foo()したがって、 3 つの値または のインスタンスを受け入れるかどうかは、プログラムで具体的Bに何foo()B意味するか、およびそれらがどのように使用されるか (能力と責任が論理的に結合されているかどうか) に依存します。

于 2013-05-23T14:31:19.487 に答える
3

この質問を考えると、クラスについて間違った方法で考えている可能性があります。

クラスを使用するときは、データ メンバーではなく、パブリック インターフェイス (通常はメソッドのみで構成される) にのみ関心を持つ必要があります。とにかく、データメンバーは通常、クラスに対してプライベートであるべきなので、それらにアクセスすることさえできません。

クラスを物理的なオブジェクト、たとえばボールと考えてください。ボールを見ると赤であることがわかりますが、単純にボールの色を青に設定することはできません。ボールを青くするには、ペイントするなどのアクションを実行する必要があります。

クラスに戻る: A が B に対して何らかのアクションを実行できるようにするために、A は B について何かを知る必要があります (たとえば、ボールの色を変更するには、ボールをペイントする必要があることなど)。

A をクラス B のオブジェクト以外のオブジェクトと連携させたい場合は、継承を使用して A が必要とするインターフェイスをクラス I に抽出し、クラス B と他のクラス C に I から継承させることができます。A は同等に機能するようになります。 BクラスもCクラスもOK。

于 2013-05-23T14:32:29.527 に答える
2

これは間違いなく、あなたが達成したいものに依存すると思います。クラスから関数にいくつかのメンバーを渡すことに問題はありません。それは本当に「FOO1」が何を意味するかに依存FOO1Bますother_data_x.

例を挙げると、A、B、FOO などの任意の名前を付ける代わりに、意味を理解できる「実際の」名前を作成します。

enum Shapetype
{
   Circle, Square
};

enum ShapeColour
{
   Red, Green, Blue
}

class Shape 
{ 
 public:
   Shape(ShapeType type, int x, int y, ShapeColour c) :
         x(x), y(y), type(type), colour(c) {}
   ShapeType type;
   int x, y;
   ShapeColour colour;
   ...
};


class Renderer
{
   ... 
   DrawObject1(const Shape &s, float magnification); 
   DrawObject2(ShapeType s, int x, int y, ShapeColour c, float magnification); 
};


int main()
{
   Renderer r(...);
   Shape c(Circle, 10, 10, Red);
   Shape s(Square, 20, 20, Green);

   r.DrawObject1(c, 1.0);
   r.DrawObject1(s, 2.0);
   // ---- or ---
   r.DrawObject2(c.type, c.x, c.y, c.colour, 1.0);
   r.DrawObject2(s.type, s.x, s.y, s.colour, 1.0);
};

[はい、それはまだかなりばかげた例ですが、オブジェクトに実際の名前がある場合は、主題について議論する方が少し理にかなっています]

DrawObject1は Shape に関するすべてを知る必要があり、( と呼ばれる 1 つのメンバー変数に格納するために) データ構造の再編成を開始する場合xypoint変更する必要があります。しかし、それはおそらくいくつかの小さな変更にすぎません。

一方、 ではDrawObject2、形状クラスを使用して好きなものをすべて再編成できます。すべてをまとめて削除し、x、y、形状、および色を個別のベクトルに含めることもできます [特に良い考えではありませんが、それが何らかの問題を解決していると考える場合どこかで、そうすることができます]。

それは主に、最も理にかなっているものに帰着します。Shape私の例では、オブジェクトをに渡すのはおそらく理にかなっていますDrawObject1。しかし、そうであるとは限らず、それを望まない場合も確かにたくさんあります。

于 2013-05-23T14:44:20.010 に答える