プライベートゲッターではなくパブリックファイターを使用する


51

ほとんどの不変のPOJOは次のように書かれています:

public class MyObject {
    private final String foo;
    private final int bar;

    public MyObject(String foo, int bar) {
        this.foo = foo;
        this.bar = bar;
    }

    public String getFoo() {
        return foo;
    }

    public int getBar() {
        return bar;
    }
}

しかし、私は次のようにそれらを書く傾向があります。

public class MyObject {
    public final String foo;
    public final int bar;

    public MyObject(String foo, int bar) {
        this.foo = foo;
        this.bar = bar;
    }
}

参照は最終的なものなので、オブジェクトは不変のままです。これにより、より少ないコードを記述でき、より短い(5文字ずつ:getおよび())アクセスが許可されます。

私が見ることができる唯一の欠点は、getFoo()何かおかしなことをするために将来の実装を変更したい場合、できないということです。しかし現実的には、オブジェクトは不変であるため、これは決して起こりません。インスタンス化中に検証し、インスタンス化中に不変の防御コピーを作成し(ImmutableListたとえばGuavaを参照)、オブジェクトfooまたはbarオブジェクトをget呼び出しの準備ができます。

欠けている欠点はありますか?

編集

私が見逃している別の欠点は、getまたはisで始まるメソッドのリフレクションを使用するシリアル化ライブラリであると思いますが、それはかなり恐ろしい習慣です...


なんてこったい?final変数をオブジェクト不変にしません。通常final、コールバックがそれらのフィールドにアクセスできるように、コールバックを作成する前にフィールドを定義するデザインを使用します。もちろん、メソッドを含むすべてのメソッドを呼び出すことができsetXます。
トマーシュザト

TomášZato@にはSETX方法はありませんStringint、またはMyObject。最初のバージョンでfinalbar = 7;、たとえば、クラス内のコンストラクター以外のメソッドがを試行しないようにするだけです。2番目のバージョンでfinalは、消費者が以下を実行できないようにする必要がありますMyObject x = new MyObject("hi", 5); x.bar = 7;
コリーケンダル

1
さて、「参照は最終的Objectなものなので、まだ不変であることに注意してください」は誤解を招きます-そのように見えるのfinal Objectは、不変であると思われますが、そうではありません。誤解して申し訳ありません。
トマーシュザト

3
基礎となる値が可変である場合、private + accessors roadはそれを防ぎません— myObj.getFoo().setFrob(...)
ベニチェルニアフスキーパスキン

回答:


38

私が考えることができる4つの欠点:

  1. 同じエンティティの読み取り専用で変更可能な形式が必要な場合、一般的なパターンは、保護されたメンバー変数を持つアクセサーのみを公開する不変クラスEntityを持ち、それを拡張してセッターを追加するMutableEntityを作成することです。あなたのバージョンはそれを防ぎます。
  2. ゲッターとセッターの使用は、JavaBeans規約に準拠しています。クラスをJSTLやELなどのプロパティベースのテクノロジーでBeanとして使用する場合は、パブリックゲッターを公開する必要があります。
  3. 実装を変更して値を取得したり、データベースでそれらを検索したい場合は、クライアントコードをリファクタリングする必要があります。アクセサー/ミューテーターアプローチでは、実装のみを変更できます。
  4. 最小限の驚き-パブリックインスタンス変数を見ると、誰がそれを変更しているのかをすぐに探し、カプセル化が失われるためにpandoraのボックスを開くのではないかと心配します。 http://en.wikipedia.org/wiki/Principle_of_least_astonishment

とは言っても、バージョンは間違いなく簡潔です。これが特定のパッケージ内でのみ使用される特殊なクラスである場合(ここではパッケージスコープがよい考えかもしれません)、これを1回限りと見なすことができます。ただし、このような主要なAPIは公開しません。


3
素晴らしい答え; 私はそれらのいくつかに意見の相違がありますが、このサイトが議論に最適なフォーラムであるかどうかはわかりません。要するに、1)不変であると想定する可変形式を使用して他の人を壊すことができます(おそらく、私の例ではクラスにfinalを追加する必要があります)。ケース、4)私は今のところ同意しますが、このような議論が最も驚くべきものを変えることができれば幸いです:)。
コリーケンダル

2
5)配列のように、本質的に可変なクラス/型を安全に公開することはできません。安全な方法は、毎回ゲッターからコピーを返すことです。
時計仕掛けのミューズ

@ Clockwork-Museは、人々が馬鹿ではないと仮定すればできます。someObj.thisList.add()にアクセスするとき、他のオブジェクトの状態を変更していることは明らかです。someObj.getThisList()。add()では不明です。ドキュメントを調べるか、ソースを調べる必要がありますが、メソッド宣言だけではわかりません。
kritzikratzi

2
@kritzikratzi-問題は、あなたのコードが馬鹿にならないことを保証できないことです。ある時点でそれ(最終的には自分自身になるかもしれません!)、そして驚きは大きな問題になる可能性があります。
時計仕掛けミューズ14年

1
まあ、させる任意の不変のタイプから可変タイプを継承することは、本質的に間違っています。不変の型は、「私は変わらない。これまで」という保証を与えることを忘れないでください。
デデュプリケーター

26

ゲッター/セッターも削除してください、あなたは大丈夫です!

これは、Javaプログラマーの間で非常に議論の多いトピックです。

とにかく、ゲッター/セッターの代わりにパブリック変数を使用する2つの状況があります:

  1. public final私にとって、これは単なる「ゲッター」よりも「不変」だということを示しています。ほとんどのIDEは、オートコンプリート中に最終修飾子を「F」で示します。setXXXがないことを検索する必要があるゲッター/セッターとは異なります。
  2. public non-final私はこれをデータクラスで気に入っています。すべてのフィールドを公開するだけです。ゲッター、セッター、コンストラクターはありません。いいえ、別に。ポージョ未満。私にとって、これはすぐに「見て、馬鹿だ。データを保持している、それだけだ。正しいデータを自分の中に入れるのはあなたの仕事だ」と合図する。Gson / JAXB /など これらのクラスをうまく処理します。彼らは書くのが至福です。彼らの目的や能力に疑いの余地はありません。そして最も重要なことは、変数を変更しても副作用がないことです。私見では、これにより、曖昧さがほとんどない非常に簡潔なデータモデルが得られますが、ゲッターとセッターには、内部で魔法が発生することがあるという大きな問題があります。

5
時々正気を見せてくれてうれしいです:)
ナビン

2
モデルが進化し、古いフィールドの1つが別のフィールドから派生する日が来るまで。後方互換性を維持したいので、古いフィールドを残しておく必要がありますが、ゲッターとセッターのコードを変更する代わりに、この古いフィールドが新しいモデル表現に従うようにするために、この古いフィールドが使用された他のすべての場所を変更する必要があります。簡単に記述できます。確かに...長期的に見れば悪夢です...だからこそ、C#プロパティアクセサーにはゲッター/セッターがあります。
ニュートピア

9

素人の言葉で:

  • 数行のコードを保存するために、カプセル化違反します。それはOODの目的に反します。
  • クライアントコードは、クラスメンバーの名前にハードカップリングされます。カップリングは悪いです。OODの全体的な目的は、結合を防ぐことです。
  • また、クラスが変更可能である必要はないことも確信しています。物事は変わります。変化は一定である唯一のものです。

6
これはカプセル化の違反ですか?両方のバージョンは、相互に外部の世界に公開されています(読み取り専用アクセス)。変化のハードカップリングと非柔軟性については正しいのですが、私はそれを議論しません。
KChaloux

4
@KChaloux「カプセル化違反」を「コードのコンパイルに失敗し、修正に時間を浪費しなければならない」と混同すると、最初に起こります。2番目は後で発生します。明確にするために、現在カプセル化はすでに違反されています。現在、クラスの内部はカプセル化されていません。しかし、カプセル化は過去に壊れていたため、将来クラスが変更されると、クライアントコードは将来壊れます。カプセル化がないため、今日のカプセル化と明日のクライアントコードの2つの異なる「ブレーク」があります。
Tulainsコルドバ

8
技術的には、ゲッターなどを使用する場合。代わりに、クライアントコードはメソッド名にハードカップリングされます。古いコードはまだ依存しているため、「非推奨」タグ/注釈付きの古いバージョンのメソッドを含める必要があるライブラリの数を確認しますそれをすることになっていた)。
JAB

5
実用的:プログラマーは、パブリックゲッター/セッターの名前を変更せずに、実際にプライベートフィールド名をどのくらいの頻度でリファクタリングしますか?私が言えることから、同じ名前を付けるコミュニティ全体の規則があります。たとえその規則がフィールド名からゲッターとセッターを生成するIDEツールの結果であるとしてもです。理論的:ただし、オブジェクト指向モデリングの観点からは、パブリックはパブリックコントラクトの一部であり、内部実装の詳細ではありません。だから誰かが最終的なフィールドを公開することに決めた場合、彼らはこれが彼らが実現することを約束する契約だと言っているのではないでしょうか?
トーマスユング

3
@ user949300学習できるように、どれを教えてください。
Tulainsコルドバ

6

私が目にする可能性のある欠点の1つは、クラス内のデータの内部表現に縛られていることです。これはおそらく大した問題ではありませんが、セッターを使用し、fooとbarが別の場所で定義された他のクラスから返されると決定した場合、MyObjectを使用するクラスを変更する必要はありません。MyObjectをタッチするだけです。ただし、裸の値を使用する場合は、MyObject iを使用したすべての場所をタッチする必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.