web-dev-qa-db-ja.com

ゲッターとセッターはどの程度正確に定義されていますか?

注:以前に同様のタイトルの質問が行われましたが、これが重複していると主張する前に全文を読んでください。

OOPのすべての人がgettersetterという用語を使用しているので、それらは明確に定義された意味を持つと思います。しかし、私が見つけたものは基本的に、多少矛盾する2つのステートメント:

(A)特定の属性への読み取りまたは書き込みアクセスを提供します。

これは、実稼働コードで見た実質的にすべてのゲッターとセッターが行うことです。ゲッターは属性の値を返し、セッターは1つの属性の値を設定します。

一方:

(B)それらは、クラス内のデータの内部表現を非表示にしながら、データへのアクセスと変更を可能にします。

Javaの例:

private double x, y;
public double getX() { return x; }
public double getY() { return y; }

これは明らかに(A)に準拠しています。ここで(B)に従って、データ表現を変更できます。

private double angle, distance;
public double getX() { return distance * Math.cos(angle); }
public double getY() { return distance * Math.sin(angle); }

XおよびYという名前の属性がなくなり、戻り値が計算の結果であるため、これは(A)に準拠しなくなりました。しかし、私はまだそれらをゲッターと呼びます。

さらにいくつかの例:

  1. これは明らかにセッターです。

    private double length;
    public void setLength(double newLength) {
        length = newLength;
    }
    
  2. ほとんどの定義では、セッターがいくつかのチェックを行うことができます。

    private double length;
    public void setLength(double newLength) {
        length = (newLength > 0) ? newLength : 0;
    }
    
  3. これについてはわかりません。 1つの属性に複数のセッターが存在する可能性があることに注意してください。

    private double length;
    public void setLengthInInches(double newLength) {
        length = 25.4 * newLength;
    }
    
  4. これは2つの属性を設定します。

    private double length;
    private UnitType lengthUnit;
    public void setLengthInInches(double newLength) {
        length = newLength;
        lengthUnit = UNIT_INCH;
    }
    
  5. 4に似ていますが、引数が2つあります。

    private double length;
    private UnitType lengthUnit;
    public void setLength(double newLength, UnitType newLengthUnit) {
        length = newLength;
        lengthUnit = newLengthUnit;
    }
    
  6. 別のバリエーション:

    private double length;
    private UnitType lengthUnit;
    public void setLength(LengthType newLength) {
        length = newLength.getValue();
        lengthUnit = newLength.getUnit();
    }
    
  7. ここでは属性は存在しませんが、計算することができます:

    private double start;
    private double end;
    public void setLength(double newLength) {
        end = start + newLength;
    }
    
  8. これはどうですか:

    private int productId;
    private Price price;
    public void setLength(double newLength) {
        productId = getProductIdByLength(newLength);
        price = BASE_PRICE + PRICE_PER_LENGTH_UNIT * newLength;
    }
    
  9. そしてこれ:

    private double length;
    public void setLength(SomeInput input) {
        length = input.doSomeComplicatedStuffToCalculateLength();
    }
    

これらのうちどれがセッターで、どれがセッターではありませんか?

4
Frank Puffer

C#/ Ruby/Python/etcが好きです。 プロパティ の用語。これは、概念を少し明確にするのに役立つと思います。 C#では、プロパティは形式化されたセッターとゲッターを作成するための優れた方法です。 Javaのプロパティは慣例の問題です(つまり、同じ名前を共有するset/getペアを持つJavaBeans)。

プロパティは、他のコードとやり取りすることを意図したlogical値を公開します。多くの例が示すように、内部表現はlogical表現と一致する必要はありません。

一般的なルールとして、プロパティの実装を可能な限りシンプルに保つことが、ソフトウェアを維持するための最良のオプションを提供することがわかります。セッター/ゲッターで完全に受け入れられるもの:

  • 内部状態の単純な表現(つまり、クラスフィールドの公開)
  • 値の制限(セッターは値を範囲内に強制します)
  • 内部単位との単純な変換(つまり、get/set XInYardsはメートル単位の内部表現をヤードに変換できます)
  • 計算された読み取り専用プロパティ

問題が発生する可能性のあるものは次のとおりです。

  • 1つを設定するときに複数のプロパティを変更する
  • ゲッターでtry/catchセマンティクスを必要とするアクションを実行する

プロパティの前提は、値の設定と取得が非常に単純で、非常に高速で、安全であるということです。複数の言語で、UI要素をプロパティ(JavaBeansも含む)に直接バインドできます。ロジックが十分に複雑である場合は、情報を割り当てまたは取得する適切な方法を提供することをお勧めします。

3
Berin Loritsch

最も基本的な定義は、ゲッターは値を取得するメソッドであり、セッターは値を変更するメソッドであることです。値がフィールドによってバッキングされていたり、実装オブジェクトに格納されていたりする必要はありません。

特に.netの場合、プロパティ(.netのゲッターとセッター)が予測どおりに動作することを保証するために従う一連のガイドラインがあります。 Property Design

最も重要な概念の1つは、プロパティは直交する必要があるということです。 1つのプロパティを設定しても、別のプロパティには影響しません。

2
Frank Hileman

(A)特定の属性への読み取りまたは書き込みアクセスを提供します。

(B)それらは、クラス内のデータの内部表現を非表示にしながら、データへのアクセスと変更を可能にします。

メソッドを介して属性にアクセスするため、技術的には両方を行います。属性の内部表現を変更した場合、データを予期される形式に変換するメソッドがあるため、この変更はゲッターメソッドを呼び出したコードに対して非表示にすることができます。

たとえば(疑似コードを許して)たとえば、最初に値を整数として保存するとします

class MySillyClass {
  Integer my_number = 39

  Integer myNumber() {
    return my_number
  }
}

しかし、それを文字列として保存することにしました。ゲッターメソッドで文字列を整数に戻すことができるため、この値が整数であることに依存しているコードは壊れません

class MySillyClass {
  String my_number = "39"

  Integer myNumber() {
    return my_number.to_integer
  }
}

ゲッターとセッターはまだ非常に悪い考えであり、オブジェクト指向設計の基本的なポイントを見逃していることを指摘しておく必要があります。オブジェクトは、システムでの動作に関連する特定の動作を公開する必要があります。ゲッターまたはセッターは、オブジェクトが保持しているコードの臭い他の誰かのデータであり、オブジェクトが正しく設計されていません

1
Cormac Mulhall

ゲッターとセッターの使用(および一般的なプロモーション)の背後にあるコードベースを進化させることができる動機について考えてみましょう。

フィールドに直接アクセスを許可すると、クラスが進化する機能が制限されます。たとえば、何かが変更されたときに気付くことができます。

ゲッターとセッターを入力します。フィールドアクセスと同じくらい簡単にすることも、必要に応じて複雑にすることもできます。クライアントがフィールドに直接アクセスする代わりにメソッドを使用すると、ゲッターとセッターの背後にあるコードを自由に進化させることができます。仮想メソッドとオーバーライドを使用することもでき、呼び出し元を変更する必要はありません。

ゲッターとセッターの目的がコードの保守のための進化的なパスを提供することであることを考えると、大まかに言えば、実装の範囲が広いゲッターとセッターを見つけることが期待されます-この進化的な機能を利用したもの(そうしなかった場合は、直接フィールドアクセスを使用することもできます)。

基本的に、ゲッターとセッターの明記されていない規約では、ゲッターによって返される値は、セッターが使用されるまで変更されず、セッターが使用されると、ゲッターは更新された値を返します。これは形式化される可能性があります(たとえば、関数型プログラミングでの Lenses のコントラクト/動作)。ただし、これがOOPで行われたことは知りません。

また、@ FrankHilemanが言うように、1つのプロパティを設定しても別のプロパティに影響を与えてはならないことを追加できます。

それでも、いくつかの組み合わせがあります。たとえば、オーバーロードされたゲッターとセッターを検討する場合があります。Fで温度を設定するためのセッターとCで温度を設定するためのセッター。これらのセッターは、どちらかのセッターが異なる値を返すことを期待します。使用されている。

これを踏まえて、すべての例を有効なゲッター/セッターに仮定しますが、ほとんどの例ではゲッターを表示しないため、対応するゲッターを想像する必要があります。

(それでも、はい、セッターなしでゲッターを使用する正当な理由があり、逆もまた同様です。)

1
Erik Eidt