getterおよびsetter内で許可されるものは何ですか?


45

私は、ゲッターとセッターのメソッドとカプセル化に関する興味深いインターネットの議論に入りました。誰かがすべきことは、それらを「純粋」に保ち、カプセル化を確保するための割り当て(セッター)または変数アクセス(ゲッター)だけだと言いました。

  • これは、最初にゲッターとセッターを持つという目的を完全に無効にし、検証と他のロジック(もちろん、奇妙な副作用なし)を許可する必要があると思いますか?
  • 検証はいつ行われますか?
    • セッター内で値を設定するとき(オブジェクトが無効な状態になるのを防ぐため-私の意見)
    • 値を設定する前、セッターの外側
    • オブジェクト内で、値が使用されるたびに
  • セッターは値の変更を許可されていますか(有効な値を標準的な内部表現に変換する可能性があります)?

18
ゲッターとセッターの最大の利点は、それらを持たないことです。つまり、セッターを離れると読み取り専用プロパティがあり、ゲッターを離れると、現在の値が誰のビジネスでもない設定オプションがあります。
マイケルボルグワード

7
@MichaelBorgwardt両方を省いて、わかりやすい「伝える、聞かない」インターフェースにします。プロパティ=潜在的なコードの匂い。
コンラッドルドルフ


2
セッターは、イベントの発生にも使用できます。
ジョンイザヤカルモナ

@KonradRudolphあなたの声明には同意しますが、「潜在的」という言葉を本当に強調したいと思います。
フィル

回答:


37

大学でC ++を学ぶときに、講師と同様の議論をしたことを覚えています。変数をパブリックにできるとき、ゲッターとセッターを使用する意味がわかりませんでした。私は長年の経験で今よりよく理解し、単に「カプセル化を維持する」と言うよりも良い理由を学びました。

ゲッターとセッターを定義することにより、一貫したインターフェースを提供するため、実装を変更する必要がある場合に、依存するコードが破損する可能性が低くなります。これは、クラスがAPIを介して公開され、他のアプリまたはサードパーティで使用される場合に特に重要です。それでは、ゲッターまたはセッターに入るものはどうでしょうか?

一般に、ゲッターは値にアクセスするための単純なバウンドパススルーとして実装するほうが適切です。これにより、ゲッターの動作が予測可能になります。一般的に言うと、計算や条件付きコードで操作される値にアクセスするためにゲッターが使用されているケースを見てきました。一般に、設計時に使用する視覚的なコンポーネントを作成する場合はあまり良くありませんが、実行時には便利なようです。ただし、これと単純なメソッドの使用との間に実際の違いはありません。ただし、メソッドを使用するときは、一般的にメソッドをより適切に命名する可能性が高いため、コードを読み取るときに「getter」の機能がより明確になります。

以下を比較してください。

int aValue = MyClass.Value;

そして

int aValue = MyClass.CalculateValue();

2番目のオプションでは、値が計算されていることが明確になりますが、最初の例では、値自体について何も知らずに値を返すだけであることがわかります。

おそらく、次のことがより明確になると主張できます。

int aValue = MyClass.CalculatedValue;

ただし、問題は、値が既に他の場所で操作されていると仮定していることです。したがって、ゲッターの場合、値を返すときに他の何かが起こっていると仮定したいかもしれませんが、プロパティのコンテキストでそのようなことを明確にすることは難しく、プロパティ名には動詞が含まれてはいけませんそうしないと、アクセス時に使用する名前に括弧を付ける必要があるかどうかを一目で理解することが難しくなります。

ただし、セッターはわずかに異なるケースです。プロパティに送信されるデータを検証するためにセッターがいくつかの追加処理を提供し、値の設定がプロパティの定義された境界に違反する場合に例外をスローすることが完全に適切です。ただし、一部の開発者がセッターに処理を追加する際に問題になるのは、何らかの方法でデータの計算や操作を実行するなど、セッターにもう少しやらせたいという誘惑が常にあることです。これは、場合によっては予測不能または望ましくない副作用を得ることができる場所です。

セッターの場合、私は常に単純な経験則を適用します。これは、データに対してできる限り実行しないことです。たとえば、通常は境界テストと丸めを許可して、必要に応じて例外を発生させるか、必要に応じて例外を賢明に回避できるようにします。浮動小数点プロパティは、例外の発生を回避するために余分な小数点以下を丸める一方で、小数点以下数桁を追加して範囲内の値を入力できるようにする良い例です。

セッター入力のある種の操作を適用すると、ゲッターと同じ問題が発生します。単にセッターに名前を付けるだけでは、セッターが何をしているかを他の人に知らせることは困難です。例えば:

MyClass.Value = 12345;

これは、セッターに与えられたときに値に何が起こるかについて何か教えてくれますか?

どうですか:

MyClass.RoundValueToNearestThousand(12345);

2番目の例では、データに何が起こるかを正確に示しますが、最初の例では、値が任意に変更されるかどうかを通知しません。コードを読むとき、2番目の例はその目的と機能がより明確になります。

これは、最初にゲッターとセッターを持つという目的を完全に無効にし、検証と他のロジック(もちろん、奇妙な副作用なし)を許可する必要があると思いますか?

ゲッターとセッターを持つことは、「純度」のためのカプセル化ではなく、クラスのインターフェイスへの変更を危険にさらすことなくコードを簡単にリファクタリングできるようにするためのカプセル化に関するものです。セッターでは検証は完全に適切ですが、呼び出しコードが特定の方法で発生する検証に依存している場合、検証の変更により呼び出しコードとの互換性が損なわれる可能性があるという小さなリスクがあります。これは一般的にまれで比較的リスクの低い状況ですが、完全を期すために注意する必要があります。

検証はいつ行われますか?

実際に値を設定する前に、セッターのコンテキスト内で検証を行う必要があります。これにより、例外がスローされた場合、オブジェクトの状態が変化せず、データが無効になる可能性がなくなります。通常、セッターコードを比較的すっきりさせるために、セッター内で最初に呼び出される別のメソッドに検証を委任する方が良いと思います。

セッターは値の変更を許可されていますか(有効な値を標準的な内部表現に変換する可能性があります)?

非常にまれなケースでは、多分。一般的には、しない方がよいでしょう。これは、別の方法に任せるのが最善の種類です。


再:セッターが不正な値渡された場合値を変更し、-1トークンまたは一部のNULLフラグを設定するのが妥当かもしれない
マーティンベケット

1
これにはいくつかの問題があります。値を任意に設定すると、意図的で不明確な副作用が生じます。また、不正なデータをより適切に処理するために使用できるフィードバックを呼び出し元のコードが受信することはできません。これは、UIレベルの値では特に重要です。公平を期すために、私が考えた例外の1つは、日付を標準の日付/時刻形式で保存しながら、入力として複数の日付形式を許可する場合です。入力データが合法であるという条件で、この特定の「副作用」は検証時のデータの正規化であると大いに主張することができます。
-S.ロビンス

はい、セッターの正当化の1つは、値を設定できる場合はtrue / falseを返す必要があるということです。
マーティンベケット

1
「浮動小数点プロパティは、例外の発生を回避するために過度の小数点以下を丸めたい場合の良い例です」どのような状況で、小数点以下の数が多すぎると例外が発生しますか?
マークバイヤーズ

2
検証の形式として、値を標準的な表現に変更すると考えています。デフォルトの欠落値(タイムスタンプに時刻のみが含まれる場合は現在の日付など)または値をスケーリングして(.93275-> 93.28%)内部整合性を確保する必要がありますが、そのような操作はAPIドキュメントで明示的に呼び出す必要があります、特にAPI内で珍しいイディオムである場合。
TMN

20

ゲッター/セッターが単純に値をミラーリングする場合、それらを保持したり、値をプライベートにしたりする意味はありません。正当な理由がある場合、一部のメンバー変数を公開しても何も問題はありません。3Dポイントクラスを記述している場合、.x、.y、.z publicを使用することは理にかなっています。

ラルフ・ウォルド・エマーソンが言ったように、「愚かな一貫性は、小さな政治家や哲学者、ジャワの設計者に愛されている小さな心のホブゴブリンです」。

ゲッター/セッターは、副作用がある場合、他の内部変数を更新し、キャッシュされた値を再計算し、無効な入力からクラスを保護する必要がある場合に役立ちます。

内部構造を隠すという彼らにとっての通常の正当化は、一般的に最も有用ではありません。例えば。これらのポイントを3つのフロートとして保存しています。リモートデータベースに文字列として保存することを決定するかもしれませんので、呼び出し元のコードに他の影響を与えずにそれを行うことができるように、それらを非表示にするゲッター/セッターを作成します。


1
うわー、Javaのデザイナーは神ですか?</ jk>

@delnan-明らかにそうではない-または彼らはより良い韻を踏む;-)
マーティンベケット

x、y、zがすべてFloat.NANである3Dポイントを作成することは有効でしょうか?
アンドリューTフィネル

4
「通常の正当化」(内部構造を隠す)はゲッターとセッターの最も有用性の低いプロパティであるというあなたの主張に同意しません。C#では、プロパティを基になる構造を変更できるインターフェイスにすることは間違いなく役立ちます。たとえば、プロパティを列挙にのみ使用したい場合は、のIEnumerable<T>ようなものに強制するよりもはるかに優れていますList<T>。私が言うデータベースアクセスのあなたの例は、単一の責任に違反している-モデルの表現とそれがどのように永続化されるかを混ぜることです。
ブランドンリントン

@AndrewFinnell -それはなど/削除/不可能有効ではないとの点にフラグを設定するのは良い方法かもしれない
マーティンベケット

8

MeyerのUniform Access Principle:「モジュールによって提供されるすべてのサービスは、ストレージまたは計算によって実装されているかどうかを裏切ることのない、統一された表記法を通じて利用可能でなければなりません。」ゲッター/セッター、別名プロパティの背後にある主な理由です。

クラスの1つのフィールドをキャッシュまたは遅延計算する場合、プロパティアクセサーのみを公開し、具象データは公開していない場合は、いつでもこれを変更できます。

私の意見では、値オブジェクト、単純な構造にはこの抽象化は必要ありませんが、本格的なクラスには必要です。


2

Eiffel言語を介して導入されたクラスを設計するための一般的な戦略は、Command-Query Separationです。アイデアは、この方法がなければならないことであるいずれかのあなたのオブジェクトについて何かを伝えるか、その両方を行うために何かをするオブジェクトを教えてくれますが、ありません。

これは、クラスのパブリックインターフェイスのみに関連し、内部表現には関連しません。データベース内の行に連動するデータモデルオブジェクトを考えます。データをロードせずにオブジェクトを作成できます。その後、初めてゲッターを呼び出すと、実際にが実行されSELECTます。オブジェクトの表示方法に関する内部の詳細を変更することはできますが、そのオブジェクトのクライアントに表示される方法は変更しません。ゲッターを複数回呼び出して、それらの結果を返すために異なる作業を行った場合でも、同じ結果を取得できる必要があります。

同様に、セッターは、オブジェクトの状態を変更するだけのように、契約的に見えます。UPDATEデータベースへの書き込みや、内部オブジェクトへのパラメーターの受け渡しなど、複雑な方法で行う場合があります。それは問題ありませんが、状態の設定とは無関係なことをするのは驚くべきことです。

Meyer(Eiffelの作成者)には、検証についても説明することがありました。基本的に、オブジェクトが静止しているときは常に、有効な状態になっている必要があります。そのため、コンストラクターが終了した直後、すべての外部メソッド呼び出しの前後(ただし、必ずしもその間ではない)、オブジェクトの状態は一貫している必要があります。

この言語では、プロシージャを呼び出すための構文と公開された属性を読み取るための構文の両方が同じように見えることに注意してください。言い換えると、呼び出し元は、メソッドを使用しているか、インスタンス変数を直接操作しているかを判断できません。この実装の詳細を隠さない言語でのみ問題が発生します。呼び出し元がわからない場合は、その変更をクライアントコードにリークすることなくパブリックivarとアクセサーを切り替えることができます。


1

別の受け入れられることは、クローン作成です。場合によっては、誰かがあなたのクラスを与えた後など、確認する必要があります。何かのリスト、彼はあなたのクラス内でそれを変更することはできません(そのリスト内のオブジェクトを変更することもできません)。したがって、セッターでパラメーターのディープコピーを作成し、ゲッターでディープコピーを返します。(不変の型をパラメーターとして使用することは別のオプションですが、上記では不可能であることを前提としています)ただし、必要でない場合はアクセサーで複製しないでください。プロパティ/ゲッターとセッターを一定のコストのオペレーションとして考えるのは簡単ですが(適切ではありません)、これはAPIのユーザーを待っているパフォーマンスの地雷です。


1

プロパティアクセサーとデータを格納するivarの間には常に1対1のマッピングはありません。

たとえば、ビューcenterの中心を格納するivarがない場合でも、ビュークラスはプロパティを提供します。設定はcenter、他のアイバーズへの変化を引き起こすようなorigintransformまたは何が、クラスのクライアントが知っているか気にしない center、それが正しく動作することを提供保存されています。べきものではないが起こる、しかし、その設定があるcenterものだが行っていることが、新しい値を保存する必要があるものは何でも超えて発生する原因となります。


0

セッターとゲッターの最大の利点は、APIを変更せずにAPIのルールを簡単に変更できることです。バグを検出した場合、ライブラリのバグを修正でき、すべての消費者にコードベースを更新させることはできません。


-4

セッターとゲッターは悪であり、フレームワーク/コンテナによって管理されるクラスでのみ使用されるべきだと私は信じがちです。適切なクラス設計では、ゲッターとセッターが得られません。

編集:このテーマに関するよく書かれた記事

Edit2:パブリックフィールドは、OOPアプローチではナンセンスです。ゲッターとセッターは悪であると言うことで、私はそれらが公共の場に置き換えられるべきであることを意味しません。


1
ゲッター/セッターを控えめに使用し、必要でない限りクラスデータを公開しないようにすることを提案している記事の要点を見逃していると思います。この私見は賢明な設計原則であり、開発者が慎重に適用する必要があります。クラスでプロパティを作成するという点では、変数を単純に公開できます、これにより、変数が設定されたときに検証を提供したり、「取得」したり、本質的にカプセル化に違反したり、潜在的に潜在的な代替ソースから値を引き出したりすることが難しくなりますメンテナンスが難しいインターフェース設計に縛られてしまう。
-S.ロビンス

@ S.Robins私の意見では、公開ゲッターとセッターは間違っています。パブリックフィールドはより間違っています(比較可能な場合)。
m3th0dman

@methodmanはい、パブリックフィールドが間違っていることに同意しますが、パブリックプロパティは有用です。そのプロパティは、その時点での特定の要件に応じて、検証またはデータの設定または返送に関連するイベントの場所を提供するために使用できます。ゲッターとセッター自体は間違っていません。一方、どのように、どのように使用されるかは、状況に応じて設計と保守性の点で劣っていると見なすことができます。:)
S.ロビンス

@ S.Robins OOPとモデリングの基本について考えてください。オブジェクトは実際のエンティティをコピーすることになっています。このタイプの操作/プロパティ(ゲッター/セッターなど)を持つ実在のエンティティはありますか?
m3th0dman

ここでコメントが多くなりすぎないように、このチャットにこの会話を取り入れてそこでコメントに対処します。
S.Robins
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.