回答:
保護されたメンバー変数を使用する必要がありますか?
状態を隠すことについてどれほどうるさいかによります。
開発者がやって来てあなたのクラスをサブクラス化するなら、彼らはそれを完全に理解していないので彼らはそれを台無しにするかもしれません。パブリックインターフェース以外のプライベートメンバーを使用すると、実装の具体的な詳細を確認できないため、後で変更する柔軟性が得られます。
今日の一般的な感覚は、それらが派生クラスとそのベースの間の過度の結合を引き起こすということです。
それらは保護されたメソッド/プロパティよりも特別な利点はありません(かつて、わずかなパフォーマンス上の利点がある可能性があります)。また、非常に深い継承が流行していた時代には、現時点では使用されていませんでした。
no particular advantage over protected methods/properties
ないno particular advantage over *private* methods/properties
?
super
構成を使用して親コンストラクタを呼び出すことができます。次に、親クラスのプライベート状態変数を初期化します。
一般に、意図的にパブリックとして考えられていないものは、プライベートにします。
派生クラスからプライベート変数またはメソッドにアクセスする必要がある状況が発生した場合は、プライベートから保護に変更します。
これはめったに起こりません-ほとんどの状況をモデル化するのに特に良い方法ではないので、私は継承のすべてで本当にファンではありません。とにかく、心配する必要はありません。
これは、大多数の開発者にとっては問題ない(そしておそらくそれを行うための最良の方法でもある)と思います。
問題の簡単な事実は 1年後に他の開発者が来て、プライベートメンバー変数にアクセスする必要があると判断した場合、彼らは単にコードを編集し、コードを保護に変更し、ビジネスを続行することです。
これに対する唯一の本当の例外は、バイナリdllをブラックボックス形式でサードパーティに出荷する場合です。これは基本的に、Microsoft、それらの「カスタムDataGridコントロール」ベンダー、および拡張ライブラリが同梱されている他のいくつかの大きなアプリで構成されています。あなたがそのカテゴリーに属していない限り、この種のことを心配する時間/労力を費やす価値はありません。
私にとって重要な問題は、変数を保護すると、サブクラスが常に範囲外に配置できるため、クラス内のメソッドがその値が範囲内にあることに依存することを許可できないことです。
たとえば、レンダリング可能なオブジェクトの幅と高さを定義するクラスがあり、それらの変数を保護する場合、(たとえば)アスペクト比を仮定することはできません。
批判的に、私は決してできませんコードがライブラリとしてリリースされた瞬間から、これらの仮定を行うアスペクト比を維持するようにセッターを更新しても、変数がセッターを介して設定されているか、または既存のコードのゲッター。
また、私のクラスのどのサブクラスもその保証を選択することはできません。サブクラスの全体的なポイントであっても、変数の値を強制できないためです。。
例として:
変数をプライベートに制限することで、セッターまたはゲッターを介して必要な動作を強制できます。
一般に、保護されたメンバー変数は、それらを使用するコードを完全に制御できるというまれなケースに留めておきます。パブリックAPIを作成している場合、私は絶対に言わないでしょう。以下では、メンバー変数をオブジェクトの「プロパティ」と呼びます。
メンバー変数をprivate-with-accessorsではなく保護した後、スーパークラスが実行できないことは次のとおりです。
プロパティが読み取られているときにオンザフライで遅延して値を作成します。保護されたゲッターメソッドを追加すると、遅延して値を作成し、それを返すことができます。
プロパティがいつ変更または削除されたかがわかります。これは、スーパークラスがその変数の状態を仮定しているときにバグを引き起こす可能性があります。変数のプロテクトセッターメソッドを作成すると、その制御が維持されます。
変数の読み取りまたは書き込み時に、ブレークポイントを設定するか、デバッグ出力を追加します。
使用する可能性のあるすべてのコードを検索せずに、そのメンバー変数の名前を変更します。
一般に、保護されたメンバー変数を作成することをお勧めするのはまれなケースだと思います。保護された変数を変更した他のいくつかのコードのバグを追跡するのに数時間かかるよりも、ゲッター/セッターを介してプロパティを公開するのに数分費やすほうがよいでしょう。それだけでなく、依存するコードを壊すことなく、将来の機能(遅延読み込みなど)を追加しないようにすることができます。今よりも後で行う方が難しいです。
設計レベルでは、保護されたプロパティを使用するのが適切かもしれませんが、実装では、これをアクセサー/ミューテーターメソッドではなく保護されたメンバー変数にマッピングする利点はありません。
保護されたメンバー変数は、クライアントコード(サブクラス)が基本クラスクラスの内部状態に効果的にアクセスできるため、重大な欠点があります。これにより、基本クラスがその不変条件を効果的に維持できなくなります。
同じ理由で、保護されたメンバー変数も、定数が保証されているか、単一のスレッドに限定されていない限り、安全なマルチスレッドコードの記述を大幅に難しくします。
アクセサー/ミューテーターメソッドは、メンテナンス中のAPIの安定性と実装の柔軟性を大幅に向上させます。
また、オブジェクト指向の純粋主義者の場合、オブジェクトは、状態の読み取り/設定ではなく、メッセージを送信することで、コラボレーション/通信を行います。
その見返りとして、それらにはほとんど利点がありません。私は必ずしも他の人のコードからそれらを削除するとは限りませんが、私はそれらを自分で使用しません。
ほとんどの場合、クラスのカプセル化をいくらか壊すので、プロテクトを使用するのは危険です。不適切に設計された派生クラスによって分解される可能性があります。
しかし、私には1つの良い例があります。たとえば、ある種の汎用コンテナーを使用できるとしましょう。内部実装と内部アクセサーがあります。ただし、そのデータへの少なくとも3つのパブリックアクセスを提供する必要があります:map、hash_map、vector-like。次に、次のようなものがあります:
template <typename T, typename TContainer>
class Base
{
// etc.
protected
TContainer container ;
}
template <typename Key, typename T>
class DerivedMap : public Base<T, std::map<Key, T> > { /* etc. */ }
template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }
template <typename T>
class DerivedVector : public Base<T, std::vector<T> > { /* etc. */ }
私はこの種のコードを1か月前以内に使用しました(そのため、コードはメモリからのものです)。考えた結果、汎用のBaseコンテナは抽象クラスである必要がありますが、たとえそれが十分に機能しても、直接Baseを使用するのは非常に面倒なので禁止する必要があります。
まとめこれで、派生クラスで使用されるデータが保護されました。それでも、Baseクラスが抽象である必要があるという事実を考慮に入れる必要があります。
protected
はカプセル化されていませんpublic
。私は間違って証明されても構わないと思っています。あなたがしなければならないのは、保護されたメンバーでクラスを書いて、私がそれを修正するのを禁止することだけです。明らかにクラスは非ファイナルでなければなりません、なぜならプロテクトを使用するすべてのポイントは継承のためです。何かがカプセル化されているか、カプセル化されていません。中間の状態はありません。
.Netアクセス修飾子の詳細については、こちらをご覧ください
保護されたメンバー変数には実際の利点や欠点はありません。特定の状況で何が必要かという問題です。一般に、メンバー変数をプライベートとして宣言し、プロパティを介して外部アクセスを可能にすることは慣例として受け入れられています。また、一部のツール(一部のO / Rマッパーなど)は、オブジェクトデータがプロパティによって表されることを期待し、パブリックまたは保護されたメンバー変数を認識しません。ただし、サブクラス(およびサブクラスのみ)が特定の変数にアクセスすることを知っている場合は、保護されていると宣言しない理由はありません。