質問
保護されたメンバー変数を使用する必要があるでしょうか?これにはどのような利点があり、どのような問題が発生する可能性がありますか?
解決
保護されたメンバー変数を使用する必要があるでしょうか?
非表示状態についてどれだけこだわるかによって異なります。
- 内部状態の漏洩を望まない場合は、すべてのメンバー変数をプライベートとして宣言することをお勧めします。
- サブクラスが内部状態にアクセスできることを特に気にしない場合は、protected で十分です。
開発者があなたのクラスをサブクラス化する場合、完全に理解していないため、クラスを台無しにしてしまう可能性があります。プライベート メンバーでは、パブリック インターフェイスを除き、物事がどのように行われているかに関する実装固有の詳細を確認できないため、後で変更する柔軟性が得られます。
他のヒント
最近の一般的な感覚は、それらが派生クラスとそのベースの間に過度の結合を引き起こすということです。
これらは、保護されたメソッド/プロパティに比べて特別な利点はありません (かつては、わずかにパフォーマンス上の利点があったかもしれません)。また、非常に深い継承が流行していた時代によく使用されましたが、現在はそうではありません。
一般に、何かが意図的に公開されると考えられていない場合は、それを非公開にします。
派生クラスからプライベート変数またはメソッドにアクセスする必要がある状況が発生した場合は、それをプライベートから保護に変更します。
これはめったに起こりません。継承はほとんどの状況をモデル化するのに特に良い方法ではないため、私は継承のまったくファンではありません。とにかく、心配しないで続けてください。
大多数の開発者にとってはこれで問題ない (そしておそらく最善の方法である) と思います。
問題の単純な事実は、, 1 年後に他の開発者が来て、あなたのプライベート メンバー変数にアクセスする必要があると判断した場合、その人はコードを編集して保護された変数に変更し、業務を続行するだけです。
唯一の本当の例外は、バイナリ DLL をブラックボックス形式でサードパーティに配布するビジネスを行っている場合です。これは基本的に Microsoft、それらの「カスタム DataGrid コントロール」ベンダー、そしておそらく拡張性ライブラリを同梱する他のいくつかの大規模なアプリで構成されています。あなたがそのカテゴリーに属さない限り、この種のことを心配するために時間や労力を費やす価値はありません。
一般に、保護されたメンバー変数は、それを使用するコードも完全に制御できるというまれなケースに留めておきます。パブリック API を作成している場合は、決して作成しないと思います。以下では、メンバー変数をオブジェクトの「プロパティ」と呼びます。
これがあなたのスーパークラスです できない メンバー変数を private-with-accessors ではなく保護した後に実行します。
プロパティの読み取り中に、その場で値を遅延作成します。protected getter メソッドを追加すると、値を遅延作成して返すことができます。
プロパティがいつ変更または削除されたかを知ることができます。これにより、スーパークラスがその変数の状態について仮定を行うときにバグが発生する可能性があります。変数に対して保護されたセッター メソッドを作成すると、その制御が保持されます。
変数の読み取りまたは書き込み時にブレークポイントを設定するか、デバッグ出力を追加します。
メンバー変数を使用する可能性のあるすべてのコードを検索せずに、そのメンバー変数の名前を変更します。
一般に、保護されたメンバー変数を作成することをお勧めするのはまれなケースだと思います。保護された変数を変更した他のコードのバグを数時間後に追跡するよりも、ゲッター/セッターを介してプロパティを公開するのに数分を費やす方が賢明です。それだけでなく、依存するコードを壊すことなく、将来の機能 (遅延読み込みなど) を追加することも保証されます。今やるよりも遅くやるほうが難しい。
設計レベルでは、保護されたプロパティを使用することが適切かもしれませんが、実装では、これをアクセサー/ミューテーター メソッドではなく保護されたメンバー変数にマッピングすることに利点はありません。
保護されたメンバー変数には、クライアント コード (サブクラス) が基本クラスの内部状態に事実上アクセスできるようになるため、重大な欠点があります。これにより、基本クラスがその不変条件を効果的に維持できなくなります。
同じ理由で、保護されたメンバー変数により、定数が保証されているか単一スレッドに限定されていない限り、安全なマルチスレッド コードを記述することが大幅に困難になります。
アクセサ/ミューテーター メソッドは、メンテナンス中の API の安定性と実装の柔軟性を大幅に向上させます。
また、オブジェクト指向の純粋主義者であれば、オブジェクトは状態を読み取ったり設定したりするのではなく、メッセージを送信することによって連携/通信します。
その代わりに、それらはほとんど利点を提供しません。他の人のコードから必ずしもそれらを削除する必要はありませんが、私自身はそれらを使用しません。
私にとっての重要な問題は、変数を保護すると、クラス内のどのメソッドにも保護を許可できなくなることです。 頼る サブクラスはいつでも値を範囲外に配置できるため、値が範囲内にあることが条件となります。
たとえば、レンダリング可能なオブジェクトの幅と高さを定義するクラスがあり、それらの変数を保護すると、(たとえば) アスペクト比について仮定を置くことができなくなります。
決定的に言えば、私はできる 一度もない アスペクト比を維持するためにセッターを更新したとしても、既存のコードで変数がセッターを介して設定されているか、またはゲッターを介してアクセスされているという保証はありません。
また、私のクラスのサブクラスも変数値を強制できないため、その保証を選択することはできません。 たとえそれがサブクラスの要点であるとしても.
例として:
- 幅と高さが保護された変数として保存されている四角形クラスがあります。
- (私のコンテキスト内での) 明らかなサブクラスは「DisplayedRectangle」クラスです。唯一の違いは、幅と高さをグラフィカル表示に有効な値に制限していることです。
- でも今はそれは無理だよ, 、私の DisplayedRectangle クラス以来 できない そのサブクラスは DisplayedRectangle として扱われながら、値を直接オーバーライドできるため、これらの値を真に制約します。
変数をプライベートに制約することで、セッターまたはゲッターを通じて必要な動作を強制できます。
ほとんどの場合、クラスのカプセル化が多少壊れてしまうため、protected を使用するのは危険です。適切に設計されていない派生クラスによってカプセル化が壊れる可能性があります。
しかし、良い例が 1 つあります。ある種の汎用コンテナができるとしましょう。これには内部実装と内部アクセサーがあります。ただし、そのデータへのパブリック アクセスを少なくとも 3 つ提供する必要があります。マップ、hash_map、ベクトルのようなもの。すると、次のようなものになります。
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 クラスは抽象クラスである必要があるという事実を考慮する必要があります。
要するに、そうです。
保護されたメンバー変数を使用すると、同じパッケージ内の任意のクラスだけでなく、任意のサブクラスからも変数にアクセスできます。これは、特に読み取り専用データの場合に非常に役立ちます。ただし、保護されたメンバー変数の使用はプライベート メンバー変数といくつかのゲッターとセッターを使用して複製できるため、これらが必ずしも必要であるとは思いません。
記録のために、「例外的なC ++」の項目24の下で、脚注の1つで、サッターは「パブリックまたは保護されたメンバー変数を持つクラスを書くことは決してありません。右?(一部のライブラリが設定したサンプルが貧弱であるにもかかわらず)。
.Net アクセス修飾子の詳細については、 ここに行きます
保護されたメンバー変数には実際の長所も短所もありません。それは、特定の状況で何が必要かによって決まります。一般に、メンバー変数をプライベートとして宣言し、プロパティを通じて外部アクセスを有効にすることが受け入れられています。また、いくつかのツール (例:一部の O/R マッパー) は、オブジェクト データがプロパティによって表されることを期待しており、パブリックまたはプロテクトされたメンバー変数を認識しません。しかし、サブクラス (そしてサブクラスのみ) が特定の変数にアクセスできるようにしたいことがわかっている場合、その変数を保護済みとして宣言しない理由はありません。