web-dev-qa-db-ja.com

プライベートvs保護-可視性の良い慣行に関する懸念

私は探していましたが、理論的な違いを知っています。

  • public-クラス/関数はメソッド/プロパティにアクセスできます。
  • protected-このクラスとサブクラスのみがメソッド/プロパティにアクセスできます。
  • private-このクラスのみがメソッド/プロパティにアクセスできます。継承されることさえありません。

それはすべてうまくあり、問題は、それらの間の実用的違いは何ですか?いつprivateを使用し、いつ__ protectedを使用しますか?これに関する標準的または許容可能な優れたプラクティスはありますか?

これまで、継承とポリモーフィズムの概念を維持するために、外部からアクセスする必要があるもの(コンストラクターやメインクラス機能など)にはpublicを使用し、内部メソッド(ロジック、ヘルパーメソッドなど)にはprotectedを使用します。私は正しい軌道に乗っていますか?

(この質問は私のためだけでなく、この参照のような質問を見たことがないので今後の参考のためにも注意してください)。

133
Madara Uchiha

いいえ、あなたは正しい軌道に乗っていません。良い経験則は、すべてをできるだけプライベートにすることです。これにより、クラスがよりカプセル化され、クラスを使用するコードに影響を与えずにクラスの内部を変更できます。

クラスを継承可能に設計する場合は、サブクラスからオーバーライドおよびアクセスできるものを慎重に選択し、その保護を行います(アクセス可能にするがオーバーライドできないようにする場合は、最後にJavaについて話します)。ただし、クラスのサブクラスを持つことに同意し、保護されたフィールドまたはメソッドがある場合、このフィールドまたはメソッドはクラスのパブリックAPIの一部であり、サブクラスを壊さずに後で変更できないことに注意してください。

継承されることを意図していないクラスは、(Javaで)finalにする必要があります。単体テストのために一部のアクセスルール(保護されたプライベート、最終的なものから非最終的なもの)を緩和し、それを文書化し、メソッドが保護されていてもオーバーライドされるべきではないことを明確にすることができます。

123
JB Nizet

ここでは主にメソッドアクセスについて説明しますが、少々程度を絞ってクラスをfinalに設定し、notメンバーアクセスと言います。

古い知恵

「正当な理由がない限り、非公開にする」

オープンソースが開発者ライブラリスペースとVCS /依存関係管理を支配する前に、それが書かれた数日で意味をなしました。 Github、Mavenなどのおかげで、ハイパーコラボレーティブになりました。当時は、ライブラリの利用方法を制限することで稼ぐお金もありました。私はおそらく、この「ベストプラクティス」を厳守して、キャリアの最初の8年または9年を過ごしました。

今日、私はそれが悪いアドバイスであると信じています。メソッドをプライベートまたはクラスfinalとしてマークする合理的な引数がある場合もありますが、それは非常にまれであり、それでもおそらく何も改善していません。

あなたは今までに持っていますか:

  • 継承や数行のコードで修正できたバグがあったライブラリなどに失望したり、驚いたり、傷ついたりしましたが、プライベート/最終メソッドとクラスのために、決して来ないかもしれない公式パッチを待たされましたか?私は持っています。
  • 著者が想像したものとは少し異なるユースケースでライブラリを使用したいが、プライベート/最終メソッドおよびクラスのために使用できませんでしたか?私は持っています。
  • 拡張性が過度に許容されている図書館などに失望したり、驚いたり、傷ついたりしましたか?私はそうではありません。

これらは、デフォルトでメソッドをプライベートとしてマークするために聞いた3つの最大の合理化です。

合理化#1:安全ではなく、特定のメソッドをオーバーライドする理由はありません

私が書いた特定のメソッドをオーバーライドする必要があるかどうかについて、私が間違っていた回数を数えることはできません。いくつかの人気のあるオープンソースのライブラリに取り組んできたので、物事を非公開にする真のコストを苦労して学びました。多くの場合、予期しない問題やユースケースに対する唯一の実用的なソリューションを排除します。逆に、私は16年以上の専門的開発でneverAPIの安全性に関連する理由でプライベートではなく保護されたメソッドをマークすることを後悔しています。開発者がクラスを拡張してメソッドをオーバーライドすることを選択した場合、彼らは意識的に「私は何をしているのか知っています」と言っています。生産性のために十分なはずです。期間。危険な場合は、クラス/メソッドのJavadocで注意してください。ドアを閉めただけでやみくもにしないでください。

デフォルトで保護されているマーキング方法は、現代のソフトウェア開発における主要な問題の1つである想像力の欠如に対する緩和策です。

合理化#2:パブリックAPI/Javadocsをクリーンに保ちます

これはより合理的であり、ターゲットオーディエンスによっては正しいことかもしれませんが、APIを実際に「クリーン」に保つコストが拡張性であることを考慮する価値があります。上記の理由により、万が一に備えてデフォルトで保護されているものをマークする方がおそらく理にかなっています。

合理化#3:私のソフトウェアは商用であり、その使用を制限する必要があります。

これも理にかなっていますが、消費者としては、制限の少ない競合他社(品質に大きな違いがないと仮定)を毎回使用します。

絶対とは絶対言うな

メソッドを非公開にしないと言っているわけではありません。より良い経験則は「正当な理由がない限りメソッドを保護する」ことだと言っています。

このアドバイスは、モジュールに分割されたライブラリまたは大規模プロジェクトで作業している人に最適です。小規模またはよりモノリシックなプロジェクトでは、とにかくすべてのコードを制御し、必要に応じてコードのアクセスレベルを簡単に変更できるため、それほど重要ではない傾向があります。それでも、私はまだ同じアドバイスをします:-)

46
Nick

プライベートフィールドの悪用をやめる!!!

ここでのコメントは、プライベートフィールドの使用に対して圧倒的に支持しているようです。さて、それから私は言いたいことがあります。

プライベートフィールドは原則として良いですか?はい。しかし、ゴールデンルールは、よくわからないときにすべてをプライベートにすることです間違いなく間違っています!問題が発生するまで、問題は表示されません。私の意見では、不明な場合はフィールドを保護対象としてマークする必要があります

クラスを拡張する2つのケースがあります。

  • 基本クラスに機能を追加したい
  • 現在のパッケージの外にある既存のクラスを変更したい(おそらくいくつかのライブラリで)

最初のケースでは、プライベートフィールドに問題はありません。人々がprivateフィールドを悪用しているという事実は、あなたがたわごとを変更できないことがわかったときにとてもイライラさせます。

車をモデル化する簡単なライブラリを考えてみましょう。

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

ライブラリ作成者は、私のライブラリのユーザーがassembleCar()の実装の詳細にアクセスする必要がある理由はないと考えました。ネジにプライベートのマークを付けましょう。

まあ、著者は間違っています。クラス全体をパッケージにコピーせずにassembleCar()メソッドのみを変更する場合は、運が悪いです。独自のscrewフィールドを書き換える必要があります。この車には数十本のネジが使用されており、それぞれに異なるプライベートメソッドで非初期化コードがいくつか含まれており、これらのネジはすべてプライベートとマークされているとします。この時点で、それは吸い始めます。

はい、あなたはライブラリ作成者がより良いコードを書いたかもしれないので、私と議論することができますので、プライベートフィールドには何の問題もありません。私はprivateフィールドがOOP。人々がそれらを使用しているとき、それは問題です。

物語の教訓は、ライブラリを書いている場合、ユーザーが特定のフィールドにアクセスしたいかどうかはわからないということです。よくわからない場合は、protectedとマークして、後でみんなが幸せになるようにします。 少なくともプライベートフィールドを乱用しないでください

ニックの答えを大いに支持します。

18
Larry

少し前に、可能な限りすべてのクラスをロックダウンすることについて話している記事を読みました。 immediate何らかのデータや機能を外部に公開する必要がない限り、すべてを最終的にプライベートにします。スコープを後でより許容できるように拡張することは常に簡単ですが、その逆ではありません。まず、finalprivateの選択をより簡単にするprotectedをできるだけ多く作成することを検討してください。

  1. すぐにサブクラス化する必要がない限り、すべてのクラスを最終クラスにします。
  2. サブクラス化してすぐにオーバーライドする必要がない限り、すべてのメソッドをfinalにしてください。
  3. メソッドの本体内で変更する必要がある場合を除き、すべてのメソッドパラメーターを最終的なものにします。

ファイナルクラスが残っている場合は、世界で何かが絶対に必要でない限り、すべてをプライベートにします。それをパブリックにします。

サブクラスを持つクラスが残っている場合は、すべてのプロパティとメソッドを注意深く調べてください。最初に、そのプロパティ/メソッドをサブクラスに公開するかどうかを検討します。その場合、サブクラスがオーバーライドのプロセスでプロパティ値またはメソッド実装を台無しにした場合、オブジェクトに大混乱をもたらすことができるかどうかを検討してください。それが可能であり、サブクラスからもクラスのプロパティ/メソッドをprotectしたい場合(皮肉なことに、私は知っています)、それをプライベートにします。それ以外の場合は、保護します。

免責事項:Javaにはあまりプログラミングしません:)

16
Anurag

いつprivateを使用し、いつprotectedを使用しますか?

Private Inheritanceは、と考えることができます。関係の観点からIS-A関係よりも。簡単に言えば、継承クラスの外部インターフェースは、継承クラスとの(目に見える)関係を持たず、private継承を使用して、Baseクラスが提供する同様の機能を実装します。

プライベート継承とは異なり、保護された継承は継承の制限された形式であり、派生クラスIS-A種類のBaseクラスであり、派生メンバーのアクセスを派生クラスのみに制限したい。

7
Alok Save

Paybillクラスが支払い請求を処理する場合、カプセル化がすべてです。製品クラスでは、請求プロセスの全プロセス、つまり支払い方法、支払い先の支払い方法が必要になるのはなぜですか。他のクラスも使用する場合はそのパブリックのみで、クラスを拡張する場合のみ制限されます。うちはマダラなので、プライベートは"limboo"のように見えます(クラスは1つだけです)。

1
ujwal dhakal