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