web-dev-qa-db-ja.com

Clean Codeが保護された変数の回避を提案するのはなぜですか?

クリーンコード は、「フォーマット」の章の「垂直距離」セクションで保護された変数を回避することを提案しています。

密接に関連する概念は、互いに垂直方向に近づけておく必要があります。明らかに、このルールは、別のファイルに属している概念に対しては機能しません。ただし、非常に正当な理由がない限り、密接に関連する概念を異なるファイルに分離しないでください。確かに、これは保護変数を回避する必要がある理由の1つです

理由は何ですか?

174
Matsemann

次の理由により、保護された変数は避けてください。

  1. [〜#〜] yagni [〜#〜] の問題につながる傾向があります。保護されたメンバーを実際に処理する子孫クラスがない限り、それをプライベートにします。
  2. [〜#〜] lsp [〜#〜] の問題が発生する傾向があります。保護された変数には、通常、固有の不変性が関連付けられています(そうでない場合、公開されます)。継承者はこれらのプロパティを維持する必要があります。これらのプロパティは、混乱したり意図的に違反したりする可能性があります。
  3. 違反する傾向があります [〜#〜] ocp [〜#〜] 。基本クラスが保護されたメンバーについてあまりにも多くの仮定を行う場合、または継承者がクラスの動作に対して柔軟性が高すぎる場合、その拡張によって基本クラスの動作が変更される可能性があります。
  4. それらは構成よりも拡張のための継承につながる傾向があります。これにより、結合が密になり、 [〜#〜] srp [〜#〜] の違反が多くなり、テストが困難になり、「継承よりも構成を優先」に該当する他の多くのものが発生します。討論。

しかし、ご覧のとおり、これらはすべて「傾向がある」のです。保護されたメンバーが最もエレガントなソリューションである場合があります。そして、保護された関数は、これらの問題が少なくなる傾向があります。しかし、注意深く扱われる原因はたくさんあります。そのような注意を必要とするものでは、人々は間違いを犯し、プログラミングの世界ではバグや設計の問題を意味します。

281
Telastyn

これは、規模を小さくして、グローバルを回避するのと同じ理由です。それは、変数が読み取られている場所、またはさらに悪いことに、書き込まれている場所をすべて見つけるのが難しく、使用方法を一貫させるのが難しいためです。 one派生クラスの明白な場所で記述され、基本クラスのone明白な場所で読み取られるように、使用法が制限されている場合、保護された変数によりコードをさらに作成できます明確かつ簡潔。複数のファイル全体で変数を無制限に読み書きしたい場合は、カプセル化することをお勧めします。

54
Karl Bielefeldt

私はその本を読んだことはありませんが、ボブおじさんが何を意味しているかを試すことができます。

protectedを何かに置くと、それはクラスがそれを継承できることを意味します。ただし、メンバー変数は、それらが含まれているクラスに属しているはずです。これは基本的なカプセル化の一部です。メンバー変数にprotectedを付けると、カプセル化が解除されます。これは、派生クラスが基本クラスの実装の詳細にアクセスできるようになったためです。通常のクラスで変数publicを作成するときに発生するのと同じ問題です。

問題を修正するには、次のように、保護されたプロパティに変数をカプセル化します。

protected string Name
{
    get { return name; }
    private set { name = value; }
}

これにより、基本クラスの実装の詳細を公開することなく、コンストラクター引数を使用して派生クラスからnameを安全に設定できます。

27
Robert Harvey

ボブおじさんの議論は主に距離の1つです。クラスにとって重要な概念がある場合、その概念をそのファイルのクラスと一緒にバンドルします。ディスク上の2つのファイルに分離されていません。

保護されたメンバー変数は2か所に散在しており、ちょっと魔法のように見えます。あなたはこの変数を参照していますが、ここでは定義されていません... where isそれは定義されていますか?そして狩りが始まります。 protectedを完全に回避する方が良い、彼の議論。

今、私はルールが常に手紙に従う必要があるとは思いません(例:protectedを使わないでください)。彼が得ているもののspiritを見てください...関連するものを1つのファイルにまとめます-プログラミング手法と機能を使用してそれを行います。過度に分析して、この詳細に巻き込まれないようにすることをお勧めします。

18
J. Polfer

すでにいくつかの非常に良い答えがここにあります。ただし、1つ追加する必要があります。

ほとんどの現代のOO言語では、各クラスを独自のファイルに配置することが適切です(Java AFAIKが必要))(要領を得ないでください) C++については、2つのファイルがよくあります)。

したがって、基本クラスの保護された変数にアクセスする派生クラスの関数を、ソースコードで変数の定義に「垂直方向に近い」状態に保つことは事実上不可能です。しかし、保護された変数は内部での使用を目的としているため、理想的にはそこに保持する必要があります。

9
Doc Brown

基本的な考え方は、保護されていると宣言されている「フィールド」(インスタンスレベルの変数)は、必要以上に見やすく、必要以上に「保護されていない」ということです。 C/C++/Java/C#には、「同じアセンブリ内の子クラスからのみアクセス可能」に相当するアクセス修飾子がないため、アセンブリ内のフィールドにアクセスできる独自の子を定義することができますが、他のアセンブリで作成された子に同じアクセスを許可しない。 C#には内部修飾子と保護修飾子がありますが、これらを組み合わせると、アクセスは「内部または保護」ではなく「内部または保護」になります。したがって、保護されたフィールドには、あなたがその子供を書いたか、他の誰かが書いたかにかかわらず、どの子供もアクセスできます。したがって、保護されているのはハッカーへの門戸です。

また、フィールドの定義によると、フィールドの変更に固有の検証はほとんどありません。 C#では、1つの読み取り専用にすることができます。これにより、値の型が実質的に一定になり、参照型を再初期化できなくなります(ただし、非常に変更可能です)が、それだけです。したがって、保護されていても、子供(信頼できない)はこのフィールドにアクセスでき、無効な値に設定して、オブジェクトの状態を矛盾させます(回避すべきもの)。

フィールドを操作するために受け入れられている方法は、フィールドをプライベートにして、プロパティやゲッターおよびセッターメソッドを使用してアクセスすることです。クラスのすべてのコンシューマーが値を必要とする場合、ゲッターを(少なくとも)パブリックにします。子供だけが必要な場合は、ゲッターを保護します。

質問に答える別のアプローチは、自分自身に尋ねることです。なぜ子メソッドのコードは、状態データを直接変更する機能を必要とするのですか?それはそのコードについて何を言っていますか?それは、その顔の「垂直距離」の議論です。親の状態を直接変更する必要がある子のコードがある場合、おそらくそのコードは最初に親に属すべきですか?

4
KeithS

それは興味深い議論です。

率直に言って、優れたツールはこれらの「垂直」問題の多くを軽減することができます。実際、私の意見では、「ファイル」の概念は、実際にはソフトウェア開発を妨げるものです。LightTableプロジェクトが取り組んでいるものです(http://www.chris-granger.com/2012/04/12/light-テーブル--- a-new-ide-concept /)。

画像からファイルを取り出すと、保護されたスコープがより魅力的になります。保護された概念は、Smalltalkなどの言語で最も明確になります。継承では、クラスの元の概念が単純に拡張されます。親クラスが行ったすべてのことを実行し、すべてを実行する必要があります。

私の意見では、クラス階層は可能な限り浅くすべきであり、ほとんどの拡張機能は合成に由来しています。そのようなモデルでは、プライベート変数がnotになる代わりに保護される理由はありません。拡張クラスが親クラスのすべての動作を含む拡張機能を表す必要がある場合(私はそうすべきであり、したがってプライベートメソッドは本質的に悪いと考えます)、拡張クラスがそのようなデータのストレージも表示しないのはなぜですか?

別の言い方をすれば、プライベート変数がプロテクト変数よりも適していると感じる場合は、継承は問題の解決策ではありません。

4
spronkey

そこに書かれているように、これは理由の1つにすぎません。つまり、論理的にリンクされたコードは、物理的にリンクされたエンティティ(ファイル、パッケージなど)内に配置する必要があります。小規模では、これは、UIを表示するクラスを持つパッケージ内にDBクエリを作成するクラスを配置しない理由と同じです。

ただし、保護された変数が推奨されない主な理由は、カプセル化が壊れる傾向があるためです。変数とメソッドの可視性は可能な限り制限する必要があります。詳細については、Joshua BlochのEffective Java、項目13-「クラスとメンバーのアクセス可能性を最小限にする」を参照してください。

ただし、この広告のすべてをギルドラインとしてのみ受け取るべきではありません。保護された変数が非常に悪い場合、そもそもそれらは言語内に配置されていなかったでしょう。保護フィールドimhoの妥当な場所は、テストフレームワークからの基本クラス内です(統合テストクラスがそれを拡張します)。

0
m3th0dman

JUnitテストに保護変数を使用すると便利な場合があります。それらをプライベートにすると、リフレクションなしではテスト中にアクセスできなくなります。これは、多くの状態変化を通じて複雑なプロセスをテストする場合に役立ちます。

保護されたゲッターメソッドを使用してそれらをプライベートにすることもできますが、変数がJUnitテスト中に外部でのみ使用される場合、それがより良い解決策であるかどうかはわかりません。

0
Nick