プライベートvs保護-可視性のグッドプラクティスの懸念[終了]


145

私は探していて、理論上の違いを知っています。

  • パブリック -任意のクラス/関数はメソッド/プロパティにアクセスすることができます。
  • protected-このクラスとサブクラスだけがメソッド/プロパティにアクセスできます。
  • プライベート -これだけのクラスはメソッド/プロパティにアクセスすることができます。それも継承されません。

それは問題なく、問題は、それらの間の実際的な違いは何ですか?いつ使用しprivate、いつ使用しprotectedますか?これよりも標準的または許容できる良い習慣はありますか?

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

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


33
それは重要ですか?OOPをサポートする言語はすべてこの問題を抱えています。私はたまたまPHPでプログラミングしていますが、この質問はOOPをサポートする言語に当てはまると思います。
マダラのゴースト

2
わかりました。タグを付け忘れたのではないかと思っています。oopタグが表示されます。
Paul Bellora

クラスのすべてのプロパティの可視性(プライベート/パブリック/保護)を設定する必要がありますか?または、可視性タイプが必要なのは一部のみですか?はいの場合、どのプロパティがクラスの最上位で可視性を設定する必要があるかをどのように決定しますか?
user4271704

1
保護されたものと私的なものの違いは明白です。サブクラスがメソッド/変数を使用する場合は保護を使用し、それ以外の場合はプライベートを使用します。具体的には、サブクラスが親で非常に類似したプライベート変数を再定義する必要がある場合は、保護するだけです。
iPherian 2016年

internalC#言語には、物理​​アセンブリ内のクラスまたはそのメンバーのアクセスレベルを制限する別のアクセス指定子があります。ただし、そのサポートや他の言語での同様のものについてはよくわかりません。
RBT

回答:


128

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

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

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


1
ここでの本当の質問は、privatevs についてprotectedですプロパティはいつ継承されたいのですか?将来ユーザーが自分のクラスを受講してそれを拡張したいのかどうか、本当にわかりません...
マダラのゴースト

16
さて、問題はユーザーオーバーライドしたいものではなく、あなたがオーバーライドできるようにしたいものです。通常、サイドを切り替えて考えることは役立ちます。そのクラスを使用してサブクラスを作成した場合、何をオーバーライドできるようにしたいですか?時々他のユーザーはまだ物を
見落とす

3
保護されたフィールドは、継承では悪い習慣です!。ご注意ください。ここに理由があります
RBT

4
継承では、プライベートフィールドは実際には不適切です。(誇張して)私の答えを読んでください。
ラリー

6
典型的なコントロールフリークの意見。
bruno desthuilliers 2017年

58

ここでは主にここでメソッドアクセスについて話していると言って、メンバーアクセスではなくクラスを最終的にマークすることで、これの前置きを述べます。

古い知恵

「正当な理由がない限り、非公開にしてください」

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

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

これまでに:

  • 継承と数行のコードで修正できるバグがあったライブラリなどに失望、驚いたり傷ついたりしたが、プライベート/ファイナルメソッドとクラスが原因で、公式パッチが来ることを待たされたのではないか。私が持っています。
  • 作者が想像していたのとは少し異なるユースケースでライブラリを使用したいのですが、プライベート/ファイナルメソッドとクラスのために使用できませんでしたか?私が持っています。
  • 拡張性に過度に寛容だった図書館などに失望、驚いたり傷つけられたりしていませんか?私はそうではありません。

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

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

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

デフォルトで保護されているマーキング方法は、現代のソフトウェア開発における主要な問題の1つである想像力の失敗を軽減します。

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

これはより合理的であり、対象とする利用者によっては正しいことでさえあるかもしれませんが、APIを「クリーン」に保つためのコストが実際に何であるかを検討する価値があります:拡張性。上記の理由から、万が一に備えてデフォルトで保護されているものにマークを付ける方がおそらく意味があります。

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

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

絶対とは絶対言うな

メソッドをプライベートとしてマークしないと言っているのではありません。より良い経験則は、「正当な理由がない限りメソッドを保護する」ことだと言っています。

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


Wrt your- Rationalization # 1保護されているものにマークを付けると、この動作は最終的なものではなく、子クラスがオーバーライドしたい場合があると思うので、明示的に選択します。よくわからない場合は、デフォルトで非公開にする必要があります。特に、デフォルトでメソッドを非仮想に保つC#のような言語では、保護されたアクセス指定子を追加するだけでは意味がありません。意図を非常に明確にするために、virtualprotectedキーワードの両方を追加する必要があります。また、人々protectedは継承よりも関連付けを好むため、デフォルトは認識が困難です
RBT

4
メソッドをオーバーライド可能にするために必要なキーワードの組み合わせは、言語詳細IMHOです。私が合理化#1に提示する反対の議論は、あなたが「私があまり確かでない場合...」とあなたが言及する場合を真っ向から狙っています。実際には、それが危険である理由を考えることができない場合は、拡張性を選択することでさらに多くのことを得ることができます。あなたが「連想」と言うとき、私はそれを作曲の同義語とみなしています。その場合、どちらの方法でもそれが重要であるとは思いません。
ニック

3
1または2またはメソッドをオーバーライドしたかっただけで、基になるクラスを「複製」しなければならなかった回数を覚えていません。そして、あなたが言ったように、私たちがこれまでよりも多くのコードを共有するという社会的傾向により、デフォルトではプライベートよりもプロテクトを使用する方がはるかに理にかなっています。つまり、独自のロジックに対してよりオープンになります。
しんこう

1
同意します。私は、JavaのBufferedReaderを調整してカスタム行区切り文字を処理したかっただけです。プライベートフィールドのおかげで、単に拡張してreadLine()をオーバーライドするのではなく、クラス全体を複製する必要があります。
ロン・ダン

21

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

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

私有地は原則的に良いのですか?はい。しかし、ゴールデンルールは、 間違いが確実でない場合にすべてを非公開にするというものです。問題が発生するまで、問題は発生しません。私の意見では、わからない場合は、フィールドを保護されているとマークする必要があります

クラスを拡張したい場合が2つあります。

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

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

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

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

ライブラリの作者は考えました:私のライブラリのユーザーがassembleCar()正しい実装の詳細にアクセスする必要がある理由はありませんか?ネジをプライベートとしてマークしましょう。

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

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

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

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


2
プライベートフィールドを使用することは、継承がクラス/オブジェクトの特定の動作を変更するための誤った抽象化であることを主に述べています(意識的に使用された場合)。変更は通常、APIを変更せずに動作が変更されるため、LSPに違反します。すばらしい答え、+ 1。
マダラのゴースト

説教!Minecraftの改造を書こうとするとき、プライベートは私の存在の悩みの種です。それを修正するためにReflectionまたはASMを使用する必要があることほど厄介なことは何もありません。
RecursiveExceptionException

私がニックの答えを理解したとき、彼は主にプロパティではなくメソッドに言及していました。メソッドを毎回プライベートに宣言するべきではなく、それらの使用法を慎重に検討する必要があることにはまったく同意しますが、クラスをより簡単に「書き換え」たいだけの理由で、プライベートで保護されたプロパティを宣言することを勧める理由は何ですか。私は毎日3度目の作業をしているので、完全にあなたの不満を共有してください。しかし、「コードがくだらないことになるので、最初から保護して簡単に書き換えられるようにしましょう」と言うだけでなく、人々に堅実なアーキテクチャを作成するように促しましょう。私はあなたの欲求不満を共有しますが。
sean662

16

少し前に、可能な限りすべてのクラスをロックダウンすることについて述べた記事を読みました。あなたが持っていない限り、最終的な、民間のすべてを作るすぐに外の世界にいくつかのデータや機能を公開する必要性を。スコープを後でより許容範囲に拡大することは常に簡単ですが、その逆はできません。まずできるだけ多くのものとして行うことを検討finalとの間で選択するようになりますprivateし、protectedはるかに簡単。

  1. すぐにサブクラス化する必要がない限り、すべてのクラスをfinalにします。
  2. サブクラス化してすぐにオーバーライドする必要がない限り、すべてのメソッドをfinalにします。
  3. メソッド本体内で変更する必要がない限り、すべてのメソッドパラメーターをfinalにします。これは、とにかくほとんどの場合少し厄介です。

最後のクラスが残っている場合は、世界で絶対に必要なものを除いて、すべてを非公開にし、公開します。

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

免責事項:私はJavaであまりプログラムしません:)


2
明確にするために:final classJavaのA は、継承できないクラスです。A final methodは非仮想です。つまり、サブクラスでオーバーライドできません(Javaメソッドはデフォルトで仮想です)。A final field/parameter/variableは不変の変数参照です(初期化後に変更できないという意味で)。
M.Stramm、2015

1
おかしなことに、私は正反対のアドバイスを見てきました。問題のオーバーライドが不変式を壊す可能性があることが確実でない限り、最終的なものは何もないということです。そうすれば、誰でも必要に応じて自由にクラスを拡張できます。
GordonM 2017

プログラミングのキャリアで、メソッドパラメーターを「最終」とマークしたことが理解に影響を与えた1つの例を挙げられますか?(コンパイラーが必要とする場合を
除く

7

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

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

プライベート継承とは異なり、保護された継承継承の制限された形式であり、派生クラスIS-Aは基本クラスの一種であり、派生メンバーのアクセスを派生クラスのみに制限する必要があります。


2
「関係の観点から実装された」というのは、HAS-A関係です。すべての属性がプライベートの場合、それらにアクセスする唯一の方法は、スルーメソッドです。これは、サブクラスにスーパークラスのオブジェクトが含まれていた場合と同じです。具体的なクラスからすべての保護された属性を削除することは、それらからのすべての継承も削除することを意味します。
shawnhcorey 2015年

2
興味深い思考プロセス- プライベート継承。@shawnhcorey HAS-A関係(アソシエーション(集約または構成の場合もあります))を指していることを明示的に示していただきありがとうございます。同じことを明確にするために、この投稿も更新する必要があると思います。
2017

1

ええと、それは、支払い請求クラスが支払いの請求を処理する場合、カプセル化に関するすべてであり、製品クラスでは、なぜ請求プロセスの全プロセス、つまり支払い方法、どこで支払うかを支払う方法が必要なのです。他のクラスも使用するクラスはpublicにすぎず、クラスを拡張するための制限だけが保護されています。あなたはマダラうちはなので、プライベートは"limboo"あなたがそれを見ることができるようなものです(あなたはシングルクラスだけをクラス化します)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.