明確にするために、私が尋ねているのは
public class A{
private/*or public*/ B b;
}
vs.
public class A{
private/*or public*/ class B{
....
}
}
私は間違いなくどちらか一方を使用するいくつかの理由を考えることができますが、私が本当に見たいのは、賛否両論が単なる学問ではないことを示す説得力のある例です。
これらは通常、クラスが外部インターフェイスの一部ではなく、別のクラスの内部実装の詳細である場合に使用されます。 Cスタイルの構造体に個別の構文がない言語では、内部データ構造をより明確にするデータのみのクラスとしてそれらを主に見てきました。また、イベントハンドラーやワーカースレッドなど、高度にカスタマイズされた1メソッドの派生オブジェクトにも役立ちます。
ツリー、リスト、グラフなどを構築するとします。ノードまたはセルの内部の詳細を外部の世界に公開する必要があるのはなぜですか?
グラフまたはリストを使用する人は、実装を使用せずに、インターフェースのみに依存する必要があります。将来的に(たとえば、配列ベースの実装からポインタベースの実装に)変更し、データ構造(それぞれ1つ)は、新しい実装に適合させるためにコードを変更する必要があります。
代わりに、プライベート内部クラスでノードまたはセルの実装をカプセル化すると、データ構造のインターフェースが残っている限り、クライアントが結果的にコードを調整せずに、必要なときにいつでも実装を自由に変更できます。手付かず。
データ構造の実装の詳細を非表示にすると、セキュリティ上の利点も得られます。クラスを配布する場合は、コンパイルされた実装ファイルと一緒に使用できるのはインターフェイスファイルのみであり、配列またはポインタを実際に使用しているかどうか誰にもわからないためです。したがって、コードを誤用したり検査したりできないため、アプリケーションをある種の悪用から、または少なくとも知識から保護します。実際的な問題に加えて、そのような場合に非常にエレガントなソリューションであるという事実を過小評価しないでください。
私の意見では、特定のタスクを許可するためにクラスで使用されるクラスに対して内部的に定義されたクラス簡単にを使用することは公正な警官です。
たとえば、2つの無関係なクラスで構成されるデータのリストをバインドする必要がある場合:
public class UiLayer
{
public BindLists(List<A> as, List<B> bs)
{
var list = as.ZipWith(bs, (x, y) => new LayerListItem { AnA = x, AB = y});
// do stuff with your new list.
}
private class LayerListItem
{
public A AnA;
public B AB;
}
}
内部クラスが別のクラスで使用されている場合は、別のクラスにする必要があります。内部クラスにロジックが含まれている場合は、それを分離する必要があります。
基本的に、これらはデータオブジェクトの穴を塞ぐのに最適だと思いますが、実際にロジックを含める必要がある場合は、変更する必要がある場合はどこを探すかを知る必要があるため、維持するのが困難です。
一部の言語(Javaなど)の内部クラスは、それらを含むクラスのオブジェクトに関連付けられており、修飾せずにメンバーを使用できます。利用可能な場合は、他の言語機能を使用して再実装するよりも明確です。
他の言語(C++など)でネストされたクラスには、そのような結びつきはありません。クラスが提供するスコープとアクセシビリティのコントロールを使用しているだけです。
Javaでは内部クラスを避けています。ホットスワップは内部クラスの存在下では機能しないためです。このような考慮事項のためにコードを危険にさらすことは本当に嫌いなので、これは嫌いですが、それらのTomcatの再起動により追加されます。アップ。
プライベート静的内部クラスの用途の1つはメモパターンです。記憶する必要のあるすべてのデータをプライベートクラスに入れ、一部の関数からObjectとして返します。外部では誰も(リフレクション、デシリアライゼーション、メモリインスペクションなしでは)それを調べたり修正したりすることはできませんが、クラスに戻すことができ、状態を復元できます。
プライベート内部クラスを使用する理由の1つは、使用しているAPIが特定のクラスからの継承を必要としているが、そのクラスの知識を外部クラスのユーザーにエクスポートしたくない場合です。例えば:
// async_op.h -- someone else's api.
struct callback
{
virtual ~callback(){}
virtual void fire() = 0;
};
void do_my_async_op(callback *p);
// caller.cpp -- my api
class caller
{
private :
struct caller_inner : callback
{
caller_inner(caller &self) : self_(self) {}
void fire() { self_.do_callback(); }
caller &self_;
};
void do_callback()
{
// Real callback
}
caller_inner inner_;
public :
caller() : inner_(*this) {}
void do_op()
{
do_my_async_op(&inner_);
}
};
この場合、do_my_async_opには、callbackタイプのオブジェクトを渡す必要があります。このコールバックには、APIが使用するpublicメンバー関数シグネチャがあります。
Do_op()がouter_classから呼び出されると、それ自体へのポインターの代わりに、必要なコールバッククラスから継承するプライベート内部クラスのインスタンスを使用します。外部クラスには、コールバックを外部クラスのprivate do_callbackメンバー関数にシャントする単純な目的で、外部クラスへの参照があります。
これの利点は、誰もがパブリックな "fire()"メンバー関数を呼び出すことができないことが確実であるということです。
C#のJon Skeetによると、ネストされたクラスを使用することが、完全に実装する唯一の方法でした レイジースレッドセーフシングルトン(第5バージョンを参照) (.NET 4まで)。
プライベートクラスと内部クラスは、カプセル化のレベルを上げ、実装の詳細を隠すために使用されます。
C++では、プライベートクラスに加えて、クラスcppの匿名名前空間を実装することで同じ概念を実現できます。 これは、実装の詳細を非表示/プライベート化するのに役立ちます。
これは内部クラスまたはプライベートクラスと同じアイデアですが、ファイルのコンパイル単位の外側では完全に見えないため、カプセル化のレベルがさらに高くなります。ヘッダーファイルに何も表示されないか、クラスの宣言で外側に表示されません。