16

不変オブジェクトの使用は、目前のプログラムが並列で実行されることを意図していない場合でも、ますます一般的になっています。それでもなお、ゲッターを使用します。これには、フィールドごとに 3 行のボイラープレートと、アクセスごとに 5 文字の追加文字が必要です (お気に入りの主流の OO 言語で)。これは些細なことのように思えるかもしれませんが、多くの編集者はとにかくプログラマーの負担のほとんどを取り除きますが、それでも一見不必要な作業です。

不変オブジェクトのフィールドへの直接アクセスとアクセサーの継続的な使用の理由は何ですか? 具体的には、ユーザーに (クライアントまたはライブラリ ライターの) アクセサーの使用を強制することには利点がありますか? もしそうなら、それらは何ですか?


一般的なオブジェクトを参照するこの質問とは異なり、不変オブジェクトを参照していることに注意してください。明確にするために、不変オブジェクトにはセッターがありません。

4

8 に答える 8

5

これは実際には言語に依存していると思います。失礼しますが、C#について少しお話しします。これは、この質問に答えるのに役立つと思うからです。

C#に精通しているかどうかはわかりませんが、C#のデザイン、ツールなどは非常に直感的で、プログラマーにとって使いやすいものです。
これを支援するC#の機能の1つ(Python、Dなどにも存在します)はプロパティです。プロパティは基本的に一対のメソッド(ゲッターおよび/またはセッター)であり、外部ではインスタンスフィールドのように見えます。プロパティに割り当てることができ、インスタンス変数のようにそこから読み取ることができます。
もちろん、内部的にはこれは方法であり、何でもできます。

しかし、C#データ型にはGetXYZ()メソッドとSetXYZ()メソッドがある場合もあり、フィールドを直接公開する場合もあります...そしてそれは疑問を投げかけます:いつどちらを行うかをどのように選択しますか?

Microsoftには、C#プロパティと、代わりにゲッター/セッターを使用する場合の優れたガイドラインがあります。

プロパティは、フィールドであるかのように動作する必要があります。メソッドができない場合は、プロパティに変更しないでください。次の状況では、メソッドはプロパティよりも優れています。

  • このメソッドは、時間のかかる操作を実行します。この方法は、フィールドの値を設定または取得するために必要な時間よりもかなり遅いと思われます。
  • メソッドは変換を実行します。フィールドにアクセスしても、格納されているデータの変換されたバージョンは返されません。
  • このGet方法には、観察可能な副作用があります。フィールドの値を取得しても、副作用は発生しません。
  • 実行の順序は重要です。フィールドの値の設定は、他の操作の発生に依存しません。
  • メソッドを2回続けて呼び出すと、異なる結果が生成されます。
  • メソッドは静的ですが、呼び出し元が変更できるオブジェクトを返します。フィールドの値を取得しても、呼び出し元はフィールドに格納されているデータを変更できません。
  • このメソッドは配列を返します。

これらのガイドラインの全体的な目標は、すべてのプロパティを外部のフィールドのように見せることであることに注意してください。

したがって、フィールドの代わりにプロパティを使用する唯一の本当の理由は次のとおりです。

  1. カプセル化したい、やだやだ。
  2. 入力を確認する必要があります。
  3. 他の場所からデータを取得する(またはデータを送信する)必要があります。
  4. 転送バイナリ(ABI)互換性が必要です。どういう意味ですか?将来、何らかの検証を追加する必要があると判断した場合(たとえば)、フィールドをプロパティに変更してライブラリを再コンパイルすると、それに依存する他のバイナリが壊れます。しかし、ソースコードレベルでは、何も変わりません(アドレス/参照を取得している場合を除いて、おそらくそうすべきではありません)。

それでは、Java /C++と不変のデータ型に戻りましょう。

これらのポイントのどれが私たちのシナリオに当てはまりますか?

  1. 不変のデータ構造の要点はデータを格納することであり、(ポリモーフィックな)動作(たとえば、文字列データ型)を持たないことであるため、適用されない場合があります。
    データを非表示にして何もしない場合、データを保存する意味は何ですか?
    ただし、適用される場合もあります(たとえば、不変のツリーがある場合)。メタデータを公開したくない場合があります。
    しかし、その場合は、公開したくないデータを明らかに非表示にし、そもそもこの質問をしていなかったでしょう。:)
  2. 適用されません。何も変更されていないため、確認する入力はありません。
  3. 適用されません。適用されない場合、フィールドを使用できません。
  4. 適用される場合とされない場合があります。

現在、JavaとC ++にはプロパティがありませんが、メソッドが代わりに使用されます。したがって、上記のアドバイスが引き続き適用され、プロパティのない言語のルールは次のようになります。

(1)ABI互換性が不要で(2)ゲッターがフィールドと同じように動作する(つまり、上記のMSDNドキュメントの要件を満たしている)場合は、ゲッターの代わりにフィールドを使用する必要があります。

理解すべき重要な点は、これはどれも哲学的ではないということです。これらのガイドはすべて、プログラマーが期待するものに基づいています。明らかに、1日の終わりの目標は、(1)作業を完了し、(2)コードを読み取り可能/保守可能に保つことです。上記のガイドは、これを実現するのに役立つことがわかっています。目標は、それを実現するために自分の好みに合った方法を実行することです。

于 2013-01-11T07:41:01.427 に答える
4

カプセル化はいくつかの有用な目的を果たしますが、最も重要な目的は情報隠蔽です。実装の詳細としてフィールドを非表示にすることにより、オブジェクトのクライアントを、実際にフィールドが存在することに依存しないように保護します。たとえば、オブジェクトの将来のバージョンでは、値を遅延して計算またはフェッチする必要がある場合があります。これは、フィールドを読み取る要求をインターセプトできる場合にのみ実行できます。

とはいえ、ゲッターが特に冗長になる理由はありません。特にJavaの世界では、「get」プレフィックスが非常によく定着している場合でも、値自体にちなんで名付けられたgetterメソッド(つまり、foo()ではなくメソッドgetFoo())を見つけることができます。これは、いくつかを節約するための優れた方法です。文字。他の多くのオブジェクト指向言語では、ゲッターを定義し、フィールドアクセスのように見える構文を使用できるため、余分な冗長性はまったくありません。

于 2013-01-11T05:54:11.160 に答える
3

不変オブジェクトは、均一性のために直接フィールド アクセスを使用する必要があります。これにより、クライアントが期待するとおりに動作するオブジェクトを設計できるためです。

すべての変更可能なフィールドがアクセサーの背後に隠され、すべての不変フィールドが隠されていなかったシステムを考えてみましょう。ここで、次のコード スニペットを検討してください。

class Node {
    private final List<Node> children;

    Node(List<Node> children) {
        this.children = new LinkedList<>(children);
    }

    public List<Node> getChildren() {
        return /* Something here */;
    }
}

の正確な実装を知らなくても、Node契約によって設計するときに行う必要があるため、 が表示される場所ではroot.getChildren()、次の 3 つのうちの 1 つが発生していると想定することしかできません。

  • 何もない。フィールドchildrenはそのまま返され、ノードの不変性が壊れるため、リストを変更することはできません。を変更するには、Listそれをコピーする必要があります。これは O(n) 操作です。
  • たとえば、次のようにコピーされますreturn new LinkedList<>(children);。これは O(n) 操作です。このリストは変更できます。
  • 変更不可能なバージョンが返されます。例: return new UnmodifiableList<>(children);. これは O(1) 操作です。繰り返しますが、これを変更するにListは、O(n) 操作でコピーする必要があります。

いずれの場合も、返されたリストを変更するには、それをコピーするために O(n) 操作が必要ですが、読み取り専用アクセスは O(1) または O(n) のいずれかになります。ここで注意すべき重要なことは、契約による設計に従うと、ライブラリの作成者がどの実装を選択したかを知ることができないため、最悪のケース O(n) を想定する必要があるということです。したがって、O(n) アクセスと O(n) で独自の変更可能なコピーを作成します。

ここで、次のことを検討してください。

class Node {
    public final UnmodifiableList<Node> children;

    Node(List<Node> children) {
        this.children = new UnmodifiableList<>(children);
    }
}

root.childrenつまり、それは でありUnmodifiableListローカルで変更可能なコピーを作成するために O(1) アクセスと O(n) を想定できます

明らかに、後者の場合のフィールドへのアクセスのパフォーマンス特性について結論を出すことができますが、前者の場合にできる唯一の結論は、最悪の場合のパフォーマンス、つまり想定しなければならないケースがはるかに遠いということです。フィールドへの直接アクセスよりも悪い。これは、プログラマーがアクセスごとに O(n) 複雑度関数を考慮に入れる必要があることを意味します。


要約すると、このタイプのシステムでは、getter が表示される場所はどこでも、getter が変更可能なフィールドに対応するか、getter が何らかの操作を実行するか (時間のかかる O(n) 防御的コピー操作であるかどうか) をクライアントが自動的に認識します。 、遅延初期化、変換、またはその他。クライアントがフィールドへの直接アクセスを確認すると、そのフィールドへのアクセスのパフォーマンス特性がすぐにわかります。

このスタイルに従うことで、プログラマーは、対話しているオブジェクトによって提供されるコントラクトに関して、より多くの情報を推測できます。UnmodifiableListこのスタイルは、上記のスニペットを interfaceに変更するとすぐにList、フィールドへの直接アクセスによってオブジェクトを変更できるため、均一な不変性も促進されます。したがって、オブジェクト階層を上から下まで不変になるように慎重に設計する必要があります。

良いニュースは、不変性のすべての利点を得るだけでなく、フィールドがどこにあるかに関係なく、フィールドにアクセスするパフォーマンス特性を、実装を見ずに、決して変更されないという確信を持って推測できることです。

于 2013-01-11T10:54:39.843 に答える
2

Joshua Bloch は、Effective Java (2nd Edition)の「項目 14: パブリック クラスでは、パブリック フィールドではなくアクセサー メソッドを使用する」で、不変フィールドの公開について次のように述べています。

public クラスがフィールドを直接公開することは決して良い考えではありませんが、フィールドが不変であれば害は少なくなります。API を変更せずにそのようなクラスの表現を変更することはできず、フィールドが読み取られたときに補助的なアクションを実行することはできませんが、不変条件を強制することはできます。

この章を次のように要約しています。

要約すると、パブリック クラスは可変フィールドを公開すべきではありません。public クラスが不変フィールドを公開することは、まだ疑わしいものの、それほど有害ではありません。

于 2013-01-13T06:35:26.307 に答える
1

(ある種の不変性を模倣するために)パブリックfinalフィールドを持つことができますが、参照されるオブジェクトがその状態を変更できないことを意味するわけではありません。場合によっては、まだ防御コピーが必要です。

 public class Temp {
    public final List<Integer> list;

    public Temp() {
        this.list = new ArrayList<Integer>();
        this.list.add(42);
    }

   public static void foo() {
      Temp temp = new Temp();
      temp.list = null; // not valid
      temp.list.clear(); //perferctly fine, reference didn't change. 
    }
 }
于 2013-01-11T05:54:58.513 に答える
0

フィールドをカプセル化し、ゲッター メソッドを介してのみ公開することは、OOP の慣習です。フィールドを直接公開する場合、これは公開する必要があることを意味します。オブジェクトの内部状態を公開するため、フィールドを public にすることはお勧めできません。

したがって、フィールド/データ メンバーを公開することは良い方法ではなく、OOP のカプセル化の原則に違反します。また、不変オブジェクトに固有のものではないと思います。これは、不変オブジェクトにも当てはまります。

@Thilo が指摘するように編集します。別の理由: フィールドがどのように格納されているかを公開する必要がないかもしれません。

ありがとう@ティロ。

于 2013-01-11T05:46:37.463 に答える