このサイトとSOには、getter/setterに関する多くのページが含まれており、それらがカプセル化を解除したり強制したりする場合があります。getter/ setterがカプセル化を解除し、回避する必要があることに同意する開発者向けです。
質問:
すべてのフィールドにゲッターとセッターがあると、それらのフィールドをパブリックにするよりカプセル化が少なくなることは確かに事実であり、この種の設計が非常に広まっているのは残念です。
すべてのプロパティに表示用のゲッターがあるクラスを設計した場合、私は誰かがゲッターを呼び出して表示以外の計算や動作に使用できることを理解しましたが、私のポイントは、すべてのプロパティにゲッターを提供することが必要だとわかった場合です。表示するプライベート変数、デザインを再考する必要がありますか?もしそうなら、どのように?
ゲッター/セッターが正当化されるかどうかではなく、オブジェクトのプロパティを表示する場合、すべてのプロパティにゲッター/セッターを提供する場合、デザインを再考する必要がありますか?
まず、なぜ状態をカプセル化するかを検討する必要があります。 この株式回答 はかなり典型的です:
カプセル化により、オブジェクトの実装をその動作から分離して、内部データへのアクセスを制限できます。この制限により、オブジェクトの動作の特定の詳細を非表示にすることができます。これにより、「ブラックボックス」を作成し、オブジェクトの内部状態をクライアントによる破損から保護できます。
gettersの適切に記述されたセットは、オブジェクトの内部状態をクライアントによる破損にさらさないことに注意してください。 (ゲッターが参照するコレクションまたは変更可能なオブジェクトの不変のコピーまたはビューを返すことを覚えておく必要があります。クライアントがそれらを介してオブジェクトの状態を変更できないことを確認する必要があります。これがないと、コストがかかる可能性があります。データのコピー。)
第二に、 あなたが参照したもの に似た答えを見ると、そうです。データをカプセル化し、データに関するすべてを1か所にまとめます。しかし、それはカップリングの増加を犠牲にして行われ、そのアプローチはクラスの責任が大幅に増加することを意味します-エンティティの状態を維持するだけでなく、エンティティが実行する可能性のあるUIのすべての操作のためのグラフィックスコントロールも作成します関与する。
最新のソフトウェアは、シンプルなOOパラダイムを超えています。シリアル化と永続化に使用されるリフレクション、およびAPIとレポート生成に使用されるスクリプト言語を含みます。したがって、 SOLIDの原則 単一の責任(エンティティクラスの唯一の仕事は一貫した状態を維持することです)および開閉(エンティティの別のビューを追加することによるアプリケーションの拡張ではエンティティクラスのコードを変更する必要はありません)は、多くの場合、すべてを移動するよりもゲッターを提供する方が適切です1つのクラスに可能な機能。
エンティティの一貫性を維持することからの表示、シリアル化、レポートの責任を分割することに対する反対論は、主に [〜#〜] yagni [〜#〜] です。時々これは本当です。現在取り組んでいるアプリケーションは、時系列データの「チャネル」を扱います。チャネルのプロパティは、約30の異なるUI画面またはレポートにあります。もちろん、ユーザーがAPIを使用してプロパティにアクセスするために作成するPythonスクリプト)。これらの機能をすべて1つにまとめる必要があります。クラス、特にエンティティのさまざまなコレクションからの一致するデータを必要とするレポートは意味をなさず、1つのオブジェクトに対して責任が大きすぎます。
セッターに関する限り、それはよりトリッキーです。私は、読み取り専用インターフェースに対して機能する変換のパイプラインとしてアプリケーションを作成する傾向があります。エンティティのレポートまたはビューを作成する変換、およびエンティティの永続的な状態を変更するためにバックエンドに渡される変換。永続エンティティを直接変更するのではなく、更新するために、資格情報と一緒にリポジトリに変更を送信します。変更はインターフェースの変更可能な実装に適用され、すべての制約はその実装によって検証され、永続状態が更新されます。監査のためにログに記録された更新。これにより、すべての問題ではなく、かなりの問題が取り除かれ、多くのユーザーのどれが更新したかを知ることが重要なアプリケーションに特化しています。
内部変数への書き込みアクセスを制限し、読み取りアクセスを許可しても、カプセル化は保持されません。外部コードは、オブジェクトの内部状態に、そのメソッドと同じように依存できるためです。
クラスのすべてのメンバー変数にゲッターがあることは、何かが間違っているという警告の兆候であるはずです。
代わりに、generateDisplayInformation()
メソッドのようなものが必要です。このメソッドは、すべての関連情報を収集し、それを何らかのデータ構造で返します(列挙型、構造体、またはメソッドのないクラスによってインデックスが付けられた配列を考えてください)。一部のインターフェイスでは、(ostream
のように)情報を書き込みます。これにより、クラスは必要に応じて内部的にデータを保存でき、状態を気にする必要がなくなります。
また、情報の収集をクラスの単一のメソッドで行うことにより、マルチスレッドコード(ほとんどのGUIアプリケーション)で情報の有効なスナップショットを常に提供できます。
データアクセスではなく、機能を提供するクラスを優先する必要があります。 getName()
のようなメソッドがあるべきではないという意味ではありません—それは単にそれをじっくりと考えるべきであることを意味します。
私の質問は、ゲッター/セッターがカプセル化を壊し、避けるべきであることに同意する開発者向けです。
ウィキペディアの カプセル化 の説明を検討してください:
- オブジェクトのコンポーネントの一部への直接アクセスを制限するための言語メカニズム。
- データを操作するメソッド(または他の関数)とデータのバンドルを容易にする言語構造。
これに基づいて、おそらくEncapsulationのより有用なサウンドバイトはImplementation Hidingでしょう。 GetterおよびSettersは、一般にアクセスおよび変更に使用されますdata(したがって、より正式な名前accessorsおよびmutators)として、ただし、必ずしもすべてのデータがクラスの実装の一部であるわけではなく、すべてのクラスの一部ではない実装は必然的にデータになります(たとえば、クラスには、その状態を内部的に変更するプライベートメソッドが含まれる場合があります)
たとえば、ネットワークを介したデータパケットの読み取りと書き込みをサポートするTcpSocket
クラスを考えてみます。このようなクラスは、ソケットのタイムアウト期間を構成するためのget
/set
メソッドのペアを公開する可能性があります。これは、ほとんどのプログラマが合理的に制御できると想定しているものです。これらの種類の構成可能なパラメーターを制御するためのget/setメソッドのペアがクラスの実装を公開するリスクがあると合理的なプログラマーが主張することはないと思います。
- 必要に応じて、ゲッターなしでGUIにデータをどのように表示しますか?私はこのリンクを見つけましたが、あなたのクラスが表示するコンポーネントを認識しているのは奇妙に思えます。
通常は、コアアプリケーションロジックやビジネスロジックを表すクラスからGUIを分離します。一般に、私はGUIコンポーネント(つまり、外観、レイアウト、データの表示とユーザーインタラクションを担当するView
コンポーネント)を、動作クラス(アプリケーションの実際の責任を負う)から明確に区別する傾向があります。機能)合理的に可能な限り。
MV *パターン (MVC/MVP/MVVM/etc。)を使用して、構成されているView Modelクラスを使用してデータをGUIビューに公開することを好みます完全に「プレーン」なデータフィールドであり、動作やメソッドは含まれていません(または少なくとも、合理的に可能な限り少ない動作です)。これらのタイプのクラスは、その目的が単純なデータクラスとして機能することであり、ビジネスロジックの実装詳細の一部を形成しないため、通常はゲッター/セッターも必要ありません。 (したがって、それらは何に対してもカプセル化を壊しません)。
GUIコードとアプリケーション/ビジネスロジックがこのように分離されているため、ビジネスロジッククラスを何十もの不要なget
/set
メソッド;実際、ビジネスロジックと直接通信するためのGUIはまったく必要ない場合もあり、GUIはWebサービスまたはデータベースと通信してビューデータを取得するだけの場合もあります。
すべてのプロパティに表示用のゲッターがあるクラスを設計した場合、私は誰かがゲッターを呼び出して表示以外の計算や動作に使用できることを理解しましたが、私のポイントは、すべてのプロパティにゲッターを提供することが必要だとわかった場合です。表示するプライベート変数、デザインを再考する必要がありますか?もしそうなら、どのように?
このタイプの設計は、GUI /プレゼンテーションロジックとコアビジネスまたはアプリケーションロジックの分離が不十分であるという症状のように思えます。 MV *パターンに関する段落で前述したように、関連するデータのcopyをクラスから個別のデータモデルにプルする方法を提供することで、設計を根本的に再考します。ビューに。これを行う最も重要な理由の1つは、コアビジネスロジックにその変更を即座に反映することなく、GUIによってデータを自由に変更できるようにすることです。
逆に、GUIがデータをビジネスロジックにPushプッシュバックする必要がある場合、おそらくビジネスクラスはそのデータのコピーを受信してそれ自体を更新できる必要があります。
データのコピーを扱うことについてのメモ;これにはさまざまな理由が考えられます。たとえば、検証手順が含まれる場合や、ユーザーが「キャンセル」または「元に戻す」ボタンを押して変更を破棄できるようにしたい場合があります。通常、ユーザーが何らかのアクティブな「保存」または「コミット」タイプのコマンド(「ファイル->保存」、「OK」など)を発行するまで、ユーザーがアプリケーションの状態を変更できないようにします。 )
この答えは、主に dlasalle's の再構成です。
UIへのデータの表示にはセッターは必要ないため、問題はゲッターに限定されます。さらに、デバッグを無視して、UIに一部のデータを表示しようとしている場合、それはおそらく「パブリック」データです。このデータを表示しようとしている場合、それはオブジェクトの仕様の一部であるため、プライベートな実装の詳細にすることはできません。このプライベートデータからデータderivedを表示しようとしている場合は、その派生データのみを公開する必要があります。したがって、この意味で「公開」されているデータのゲッターのみを作成することが提案されていると想定します。
オブジェクトがそれ自体を提示できるようにしたい場合は、thatを実行するメソッドを作成する必要があります。これは、dlasalleが意味することです。「データアクセスではなく、機能を提供するクラスを優先する必要があります。」代わりに一連のゲッターを提供することは必要でも十分でもありません。それは、a)ある種の「プレゼンテーションオブジェクト」を返すために1つのメソッドを追加するだけで十分であり、b)プレゼンテーションオブジェクト自体が特定のフィールドへのアクセスを提供しない場合があるためです(柔軟性のためと思われます)。 「UIビルダーオブジェクト」を受け取り、それを使用してUIを生成するメソッドを持つことができます。 dlasalleが見事に指摘しているように、一連のゲッターを呼び出してもオブジェクトの状態の一貫した図が得られる保証はないため、これでは不十分です。これは特に並行コンテキストで当てはまりますが、順次コンテキストでも当てはまります。この場合も、オブジェクトの責任の1つがその状態の一貫したスナップショットを提供することである場合、これはそのような保証を提供する明示的なメソッドである必要があります。 (たとえば、フィールドへのアクセスにデータベースルックアップが必要かどうかを想像してください。別々のデータベーストランザクションで各フィールドにアクセスすると、原子性と分離の保証が失われます。)
オブジェクトの状態のスナップショットを提供する/レポートを提供する/その状態のシリアル化を提供するオブジェクトの類似性が示唆されます。メソッドが生成する「プレゼンテーションオブジェクト」は不変オブジェクトである必要があることを示唆しています。このオブジェクトは概念的には「純粋なデータ」のみで構成されている必要があります。つまり、値オブジェクトである必要があります。元のオブジェクトの変化に応じてUIを変更したい場合、オブジェクトは「プレゼンテーションオブジェクト」のストリームを公開する必要があります(おそらく ReactiveX のようなものを使用します)。