明確にするために、私が尋ねているのは
public class A{
private/*or public*/ B b;
}
対
public class A{
private/*or public*/ class B{
....
}
}
どちらか一方を使用するいくつかの理由を明確に考えることができますが、私が本当に見たいのは、長所と短所が単なる学術的ではないことを示す説得力のある例です。
明確にするために、私が尋ねているのは
public class A{
private/*or public*/ B b;
}
対
public class A{
private/*or public*/ class B{
....
}
}
どちらか一方を使用するいくつかの理由を明確に考えることができますが、私が本当に見たいのは、長所と短所が単なる学術的ではないことを示す説得力のある例です。
回答:
通常、クラスが外部インターフェイスの一部ではなく、別のクラスの内部実装の詳細である場合に使用されます。私は主に、Cスタイル構造体用の個別の構文を持たない言語で内部データ構造をきれいにするデータ専用クラスとして見てきました。また、イベントハンドラーやワーカースレッドなど、高度にカスタマイズされた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;
}
}
内部クラスが別のクラスで使用されている場合は、別個に配置する必要があります。内部クラスにロジックが含まれている場合は、個別に配置する必要があります。
基本的には、データオブジェクトの穴を塞ぐのに優れていると思いますが、実際にロジックを含める必要がある場合は、変更する必要がある場合にそれらを探す場所を知る必要があるため、メンテナンスが困難です。
tuple.ItemX
コードが不明瞭になります。
ホットスワップは内部クラスが存在すると機能しないため、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には、コールバックタイプのオブジェクトを渡す必要があります。このコールバックには、APIが使用パブリックメンバー関数シグネチャがあります。
do_op()がouter_classから呼び出されると、それ自体へのポインターの代わりに、必要なコールバッククラスから継承するプライベート内部クラスのインスタンスを使用します。外部クラスには、外部クラスのプライベート do_callbackメンバー関数にコールバックを迂回させるという単純な目的のために、外部クラスへの参照があります。
この利点は、他の誰もパブリックな「fire()」メンバー関数を呼び出せないことを確信していることです。
C#の詳細なJon Skeetによると、ネストクラスを使用することが、完全に遅延スレッドセーフなシングルトンを実装する唯一の方法でした(第5バージョンを参照)(.NET 4まで)。
プライベートクラスと内部クラスは、カプセル化のレベルを上げ、実装の詳細を隠すために使用されます。
C ++では、プライベートクラスに加えて、クラスcppの匿名名前空間を実装することで同じ概念を実現できます。これは、実装の詳細を非表示/プライベート化するのに役立ちます。
内部クラスまたはプライベートクラスと同じ考え方ですが、ファイルのコンパイルユニットの外では完全に見えないため、カプセル化のレベルはさらに高くなります。ヘッダーファイルには何も表示されず、クラスの宣言では外部からは見えません。