いつプライベート/内部クラスを使用すべきですか?


21

明確にするために、私が尋ねているのは

public class A{
    private/*or public*/ B b;
}

public class A{
    private/*or public*/ class B{
        ....
    }
}

どちらか一方を使用するいくつかの理由を明確に考えることができますが、私が本当に見たいのは、長所と短所が単なる学術的ではないことを示す説得力のある例です。


2
答えは、言語およびコアライブラリによって提供される機能に依存します。C#言語を想定して、StyleCopでこれらを実行してみてください。このクラスを独自のファイルに分離することについて警告が表示されることは間違いありません。つまり、MSFTで十分な人が、使用した例のいずれかを使用する必要がないと考えることを意味します。
仕事

アカデミックな例では不十分な場合、説得力のある例は何でしょうか?

@Job StyleCopは、内部クラスが外部から見える場合にのみ警告を出します。プライベートの場合、完全に問題なく、実際には多くの用途があります。
julealgon

回答:


20

通常、クラスが外部インターフェイスの一部ではなく、別のクラスの内部実装の詳細である場合に使用されます。私は主に、Cスタイル構造体用の個別の構文を持たない言語で内部データ構造をきれいにするデータ専用クラスとして見てきました。また、イベントハンドラーやワーカースレッドなど、高度にカスタマイズされた1メソッドから派生したオブジェクトにも役立つ場合があります。


2
これは私がそれらを使用する方法です。機能的な観点から、クラスが別のクラスのプライベート実装の詳細に過ぎない場合、プライベートメソッドまたはフィールドと同様に、そのように宣言する必要があります。他の場所で使用されることのないガベージで名前空間を汚染する理由はありません。
アーロンノート

9

ツリー、リスト、グラフなどを構築しているとします。ノードまたはセルの内部詳細を外部世界に公開する必要があるのはなぜですか?

グラフやリストを使用している人は、将来的にはインターフェースを変更する必要があります(たとえば、配列ベースの実装からポインターベースの実装に変更する)ため、インターフェースではなく実装に依存する必要があります。データ構造(それぞれ)は、新しい実装に合わせてコードを変更する必要があります。

代わりに、プライベートな内部クラスにノードまたはセルの実装をカプセル化することにより、データ構造のインターフェイスが残っている限り、クライアントが結果的にコードを調整することなく、必要に応じて実装を自由に変更できますそのまま。

データ構造の実装の詳細を非表示にすることもセキュリティ上の利点につながります。クラスを配布したい場合は、コンパイル済みの実装ファイルと一緒に使用できるのはインターフェースファイルのみであり、実際に配列またはポインターを使用しているかどうかは誰にもわからないためです実装のために、コードを悪用したり検査したりすることが不可能なため、何らかの悪用または少なくとも知識からアプリケーションを保護します。実際的な問題に加えて、このような場合に非常にエレガントなソリューションであるという事実を過小評価しないでください。


5

私の意見では、特定のタスクを許可するためにクラスで短時間使用されるクラスに内部定義されたクラスを使用するのは公正な警官です。

たとえば、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;
    }
}

内部クラスが別のクラスで使用されている場合は、別個に配置する必要があります。内部クラスにロジックが含まれている場合は、個別に配置する必要があります。

基本的には、データオブジェクトの穴を塞ぐのに優れていると思いますが、実際にロジックを含める必要がある場合は、変更する必要がある場合にそれらを探す場所を知る必要があるため、メンテナンスが困難です。


2
C#を想定して、ここでタプルを使用することはできませんか?
仕事

3
@Job Sure、しかし時にはtuple.ItemXコードが不明瞭になります。
ラッセエスペホルト

2
@Job yup、lasseespeholtはそれを正しかった。私はむしろ{string Title;の内部クラスを見たいです。文字列説明; } Tuple <string、string>より。
エドジェームズ

その時点で、そのクラスを独自のファイルに入れても意味がありませんか?
ジョブ

いいえ、親クラスの権限の外に出ないタスクを達成するために、それを実際に使用して一部のデータを簡単に結び付けるだけではありません。少なくとも、私の意見ではありません!
エドジェームズ

4
  • 一部の言語(たとえばJava)の内部クラスは、含まれるクラスのオブジェクトと結びついており、修飾せずにメンバーを使用できます。可能な場合は、他の言語機能を使用して再実装するよりも明確です。

  • 他の言語(たとえばC ++)のネストされたクラスには、このような結び付きはありません。クラスが提供するスコープとアクセシビリティコントロールを使用しているだけです。


3
実際、Javaにはこれらの両方があります。
ヨアヒムザウアー

3

ホットスワップは内部クラスが存在すると機能しないため、Javaの内部クラスは避けます。そのような考慮事項のためにコードを危うくすることは本当に嫌いなので、これは嫌いですが、Tomcatの再起動は増えます。


この問題はどこかに文書化されていますか?
-gnat

Downvoter:私の主張は間違っていますか?
ケビンクライン

1
あなたの答えには詳細が欠けているからではなく、間違っているからではありません。詳細がないと、アサーションの検証が難しくなります。あなたがその問題により追加した場合、私はあなたの情報のルックスはかなり面白いので、おそらく、upvoteに戻るだろうと有用である可能性がある
ブヨ

1
@gnat:私は少し研究をしましたが、これはよく知られている真実のように思えますが、Java HotSwapの制限に関する明確なリファレンスは見つかりませんでした。
ケビンクライン

我々はSkeptics.SE上じゃない:)だけで、いくつかの参照はどうしたら、それは決定的である必要はない
ブヨ

2

プライベート静的内部クラスの用途の1つは、メモパターンです。覚えておく必要があるすべてのデータをプライベートクラスに入れ、Objectとして何らかの関数からそれを返します。外部の誰も(リフレクション、デシリアライズ、メモリ検査などなしに)それを調べたり変更したりすることはできませんが、クラスに返してその状態を復元することはできます。


2

プライベート内部クラスを使用する理由の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()」メンバー関数を呼び出せないことを確信していることです。


2

C#の詳細なJon Skeetによると、ネストクラスを使用することが、完全に遅延スレッドセーフなシングルトンを実装する唯一の方法でした(第5バージョンを参照)(.NET 4まで)。


1
それの初期化オンデマンド・ホルダーイディオムは、Javaでそれはまた、プライベート静的内部クラスが必要です。これは、中にお勧めしますJMMよくある質問でブライアン・ゲッツにより、JCiP 16.4にし、ジョシュア・ブロックでのJavaOne 2008より効果的なJavaのダウンロード(PDF)「...静的フィールド上高性能のために」
ブヨ

1

プライベートクラスと内部クラスは、カプセル化のレベルを上げ、実装の詳細を隠すために使用されます。

C ++では、プライベートクラスに加えて、クラスcppの匿名名前空間を実装することで同じ概念を実現できます。これは、実装の詳細を非表示/プライベート化するのに役立ちます。

内部クラスまたはプライベートクラスと同じ考え方ですが、ファイルのコンパイルユニットの外では完全に見えないため、カプセル化のレベルはさらに高くなります。ヘッダーファイルには何も表示されず、クラスの宣言では外部からは見えません。


-1に関する建設的なフィードバックはありますか?
ハイウェイロン

1
私は投票しませんでしたが、あなたは本当に質問に答えているわけではないと思います:あなたはプライベート/内部クラスを使用する理由を与えていません。あなただけ++、それがどのように動作するかを説明し、どのようにCに似たことをしている
サイモンBergot

確かに。私はそれが私のものと他の答え(実装の詳細を隠し、カプセル化のレベルを上げるなど)によって明白だと思ったが、いくつかを明確にしようと思う。@Simonに感謝します。
ハイウェイロン

1
素敵な編集。そして、このような反応のために攻撃をしないでください。SOネットワークは、信号対雑音比を改善するためのポリシーを実施しようとしています。一部の人々は質問/回答の範囲についてかなり厳しく、いつかいい人であることを忘れる場合があります(たとえば、下票をコメントすることによって)
サイモン

@サイモンありがとう。通信品質が低いため、期待はずれです。ウェブの匿名のベールの背後にある「厳格な」ことは少し簡単すぎるかもしれません。まあ、新しいことは何もないと思います。正のフィードバックをありがとう。
ハイウェイロン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.