web-dev-qa-db-ja.com

ゲッターとセッターのデザインは貧弱ですか?矛盾したアドバイスが見られた

私は現在、いくつかの異なるモードでJavaのシンプルなゲームに取り組んでいます。メインのGameクラスを拡張して、他のクラス内にメインロジックを配置しました。それにもかかわらず、メインのゲームクラスはまだかなり重いです。

私のコードを簡単に見てみると、ゲームのロジックに本当に必要な残りと比較して、その大部分はゲッターとセッター(60%)でした。

いくつかのGoogle検索では、ゲッターとセッターは悪であると主張していますが、他の人は、良いOO練習と素晴らしいプログラムに必要だと主張しています。

だから私は何をすべきですか?どっちがいい?私のプライベート変数のゲッターとセッターを変更する必要がありますか、それとも固執するべきですか?

223
Mike B

また、ほとんどの場合、セッターを使用すると、意味のない値を設定できるため、カプセル化が中断されるという観点もあります。非常に明白な例として、ゲームにスコアカウンターがあり、それが上昇するのではなく、

// Game
private int score;
public void setScore(int score) { this.score = score; }
public int getScore() { return score; }
// Usage
game.setScore(game.getScore() + ENEMY_DESTROYED_SCORE);

そのはず

// Game
private int score;
public int getScore() { return score; }
public void addScore(int delta) { score += delta; }
// Usage
game.addScore(ENEMY_DESTROYED_SCORE);

これはおそらく少し簡単な例です。私が言おうとしていることは、ゲッター/セッターとパブリックフィールドを議論することは、オブジェクトがお互いの内部状態を密接に操作し、それにより密接に結びついているという大きな問題をあいまいにすることが多いということです。

アイデアは、やりたいことを直接行うメソッドを作成することです。例は、敵の「生きている」ステータスを設定する方法です。 setAlive(boolean alive)メソッドを使用したくなるかもしれません。代わりに次のものが必要です。

private boolean alive = true;
public boolean isAlive() { return alive; }
public void kill() { alive = false; }

これは、「生きている」ブール値ではなく「ヒットポイント」値を持たない実装を変更する場合、前に書いた2つのメソッドの規約を破ることなく、それを変更できるからです。

private int hp; // Set in constructor.
public boolean isAlive() { return hp > 0; } // Same method signature.
public void kill() { hp = 0; } // Same method signature.
public void damage(int damage) { hp -= damage; }
344
Zarkonnen
  • 非常に悪い:パブリックフィールド。
  • やや悪:ゲッターとセッターは必要ありません。
  • 良い:ゲッターとセッターは本当に必要な場合にのみ-タイプを操作する状態のリポジトリとして扱うのではなく、seその状態に起こる「より大きい」動作をタイプに公開する他のタイプ。

それは本当に状況に依存します-時々あなたは本当にdoただ愚かなデータオブジェクトが欲しいだけです。

180
Jon Skeet

あなたはすでにこれについて多くの良い答えを持っているので、私はちょうど2セントを差し上げます。ゲッターとセッターは非常に悪です。ほとんどの場合、内部状態を非表示にしない冗長なコードに放り込まれたときに、オブジェクトの内部を非表示にするふりをすることができます。単純なPOJOの場合、getName()とsetName()をobj.name = "Tom"に置き換えることができない理由はありません。

メソッド呼び出しが割り当てに置き換わるだけの場合、メソッド呼び出しを好むことで得られるのはコードの肥大化だけです。残念ながら、この言語はJavaBeans仕様でゲッターとセッターの使用を禁じているため、Javaプログラマーは、たとえそうすることが意味をなさないとしても、それらの使用を強制されます。

幸いなことに、Eclipse(およびおそらく他のIDE)でも自動的に生成できます。そして、楽しいプロジェクトのために、私はかつてXSLTでそれらのコードジェネレーターを構築しました。しかし、Javaで1つ削除したいことがあるとしたら、それはゲッターとセッターへの過度の依存です。

46
rtperson

ゲッターとセッターは、オブジェクト指向プログラミングで カプセル化 の概念を実施します。

オブジェクトの状態を外の世界から隠すことにより、オブジェクトはそれ自体を完全に担当し、意図しない方法で変更することはできません。オブジェクトを操作できる唯一の方法は、getterやsetterなどの公開されたパブリックメソッドを使用することです。

ゲッターとセッターを持つことにはいくつかの利点があります。

1。変更されたクラスを使用するコードを変更せずに将来の変更を許可します。

ゲッターとセッターを使用する大きな利点の1つは、パブリックメソッドが定義され、基礎となる実装の変更が必要になるときがあることです(たとえば、パフォーマンスを改善するための別のアルゴリズムを使用して、修正が必要なバグを見つけること)など)、ゲッターとセッターをオブジェクトを操作する唯一の方法にすることで、既存のコードが壊れず、変更後も期待どおりに動作します。

たとえば、オブジェクトにsetValueプライベート変数を設定するvalueメソッドがあるとします。

public void setValue(int value)
{
    this.value = value;
}

しかし、その後、valueが変更された回数を追跡する必要がある新しい要件がありました。セッターを配置すると、変更は非常に簡単です。

public void setValue(int value)
{
    this.value = value;
    count++;
}

valueフィールドがパブリックの場合、後で戻って値が変更された回数を追跡するカウンターを追加する簡単な方法はありません。したがって、ゲッターとセッターを持つことは、後で発生する可能性のある変更に対してクラスを「将来に耐える」方法の1つです。

2。オブジェクトを操作できる手段を強制します。

ゲッターとセッターが役立つもう1つの方法は、オブジェクトの操作方法を実施することです。したがって、オブジェクトは自身の状態を制御します。公開されたオブジェクトのパブリック変数を使用すると、簡単に破損する可能性があります。

たとえば、ImmutableArrayオブジェクトには、intというmyArray配列が含まれます。配列がパブリックフィールドである場合、不変ではありません。

ImmutableArray a = new ImmutableArray();
int[] b = a.myArray;
b[0] = 10;      // Oops, the ImmutableArray a's contents have been changed.

真に不変の配列を実装するには、配列のコピーを返すように、配列のゲッター(getArrayメソッド)を記述する必要があります。

public int[] getArray()
{
    return myArray.clone();
}

また、次のことが発生した場合でも:

ImmutableArray a = new ImmutableArray();
int[] b = a.getArray();
b[0] = 10;      // No problem, only the copy of the array is affected.

ImmutableArrayは確かに不変です。オブジェクトの変数を公開すると、意図しない方法で操作できますが、特定の方法(ゲッターとセッター)を公開するだけで、オブジェクトは意図した方法で操作できます。

ゲッターとセッターを持つことは、他の人が使用するAPIの一部であるクラスにとってより重要だと思う。

ゲッターとセッターのすべての利点がありますが、ゲッターがプライベート変数の値を返すだけで、セッターが値を受け入れてプライベート変数に割り当てるだけの場合、ゲッターとセッターは単に無関係であり、実際には無駄。クラスが、他の人によって使用されないアプリケーションによる内部使用のためだけである場合、ゲッターとセッターを広範囲に使用することは、パブリックAPIを書くときほど重要ではないかもしれません。

28
coobird

彼らは絶対に悪です。

@coobirdは残念ながら絶対に「カプセル化の概念を強制する」わけではありません。実際にメソッドの壮大さの妄想を持つプロパティを介してデータを公開しているときに、データをカプセル化していると思わせるだけです。ゲッター/セッターがパブリックフィールドで行うことはすべて、より優れています。

まず、公開データが必要な場合は、公開データを取得し、ゲッターメソッドとセッターメソッドを削除して、クライアントがウェイドする必要のあるメソッドの数を減らし、クライアントがその値を変更するなどの認識を簡単にします。

object.field = value;

より認知的に強いのではなく

object.setField(value);

クライアントは、ゲッター/セッターメソッドに副作用があるかどうかを確認する必要があります。

第二に、メソッドで本当に何か他のことをする必要がある場合、単に取得または設定するよりも多くの責任があるときに、それをget/setメソッドと呼ぶのはなぜですか? SRPに従うか、実際に何かを伝えるメソッドを呼び出します 全体 この方法は、彼が言及したザルコネンの例に似ています。

public void kill(){
    isAlive = false;
    removeFromWorld(this);
}

の代わりに

public void setAlive(boolean isAlive){
    this.isAlive = isAlive;
    if (isAlive)
        addToWorld(this);
    else
        removeFromWorld(this);
}

setAlive(boolean)メソッドは、副作用としてオブジェクトをワールドから削除することをクライアントにどこで伝えますか?なぜクライアントはisAliveフィールドに関する知識を持つ必要があるのですか?さらに、オブジェクトをワールドに再度追加するとどうなりますか?再初期化する必要がありますか?なぜクライアントはそれを気にしますか?

私見のモラルは、メソッドに名前を付けて、正確に何をするかを言い、SRPに従い、ゲッター/セッターを取り除きます。ゲッター/セッターなしで問題が発生した場合、オブジェクトに他のクラスで何かをしようとするのではなく、独自のクラス内で独自のダーティな作業を行うように指示します。

ここで私の暴言を終わらせる、それについて申し訳ありません;)

27
nathan

滑りやすい斜面です。

単純なTransferオブジェクト(またはParameterオブジェクト)には、いくつかのフィールドを保持し、その値をオンデマンドで提供するという唯一の目的がある場合があります。ただし、その縮退した場合でも、オブジェクトは不変である必要があると主張できます-コンストラクターで構成され、get...メソッドのみを公開します。

また、いくつかの「コントロールノブ」を公開するクラスの場合もあります。カーラジオのUIは、おそらくgetVolumesetVolumegetChannel、およびsetChannelのようなものを公開していると理解できますが、実際の機能は信号を受信して​​音を発しています。しかし、これらのノブは実装の詳細をあまり公開しません。これらのインターフェース機能からは、ラジオがトランジスターか、ほとんどがソフトウェアか、真空管かはわかりません。

オブジェクトを問題領域のタスクのアクティブな参加者と考えるようになると、オブジェクトについて尋ねるのではなく、何かをするを求めるという点で考えるようになります。内部状態、またはデータを要求する他のコードはそれらの値で何かをすることができます。

だから...「悪」?あんまり。しかし、値を入力し、その値のget...とset...の両方のメソッドを公開しようとするたびに、理由と、そのオブジェクトの責任が本当に何であるかを自問してください。あなたが自分に与えることができる唯一の答えが「私のためにこの値を保持するために」である場合、多分 OO以外の何かがここで起こっています。

19
joel.neely

Gameクラスは、多くの変数を公開している場合、おそらく god object antipatternに従っています。ゲッターとセッターには何の問題もありません(Javaの冗長性は少し面倒な場合があります)。各クラスが明確に分離された機能を持つ適切に設計されたアプリでは、単一のクラスでそれらの数十を必要としません。

編集:ゲッターとセッターの主なポイントがゲームクラスを「構成する」ことである場合(私はあなたのコメントをそのように理解しています)、おそらくあなたはドンゲッターは必要ありません(クラスがgetメソッドを使用せずに独自のプライベート変数にアクセスするのは完全に問題ありません)。おそらく、セッターの多くを概念的に一緒に属するいくつかの変数を設定する「グループセッター」にまとめることができます。

15

ゲッターとセッターの存在は、設計上の問題があることを示す傾向があります(そのような小学校言語に興味がある場合は「におい」)。取るに足らないゲッターとセッターは、パブリックフィールドとほとんど区別できません。通常、データを操作するコードは別のクラスになります-カプセル化が不十分であり、プログラマーにオブジェクト指向では安心できないことを期待します。

場合によっては、ゲッターとセッターで十分です。しかし、原則として、ゲッターとセッターの両方を持つ型は設計上の問題を示します。ゲッターは不変性のために働きます。セッターは「聞かないで」と言います。不変性と「聞かない」は、重複するスタイルで適用されない限り、良い設計選択です。

11

私の意見では、ゲッターとセッターは優れたプログラムの要件です。それらに固執しますが、不必要なゲッター/セッターを作成しないでください-すべての変数を直接処理する必要は必ずしもありません。

10
Joonas Pulakka

私は本当に彼らが悪だとは思わない。しかし、本当に必要な場合を除いて、それらを使用する必要のない世界に住みたいと思っています。

私が上で読んだ1つの例は、future-proofingあなたのコードでした。例えば:

public void setValue(int value)
{
    this.value = value;
}

次に、要件が変わり、値が設定された回数を追跡する必要があります。

そう:

public void setValue(int value)
{
    this.value = value;
    count++;
}

美しいです。わかった。しかし、Rubyでは、次のものは同じ目的を果たしませんか?

someobject.my_value = 100

後で、my_valueが設定された回数を追跡する必要があります。それでは、セッターTHENをオーバーライドするだけでなく、THENだけをオーバーライドできませんか?

def my_value=(value)
    @my_value = value
    @count++
end

私はすべて美しいコードを求めていますが、Javaクラスの山を見て、文字通り何千、何千行ものコードが見られますが、基本的なゲッター/セッターはくて面倒です。

私がフルタイムでC#を開発していたとき、私たちは常にパブリックプロパティを使用し、必要な場合にのみカスタムgetter/setterを実行しました。魅力のように働き、それは何も壊しませんでした。

9
cbmeeks

いつものように、唯一の答えは次のとおりです。あなたがコードに触れている唯一のペロンなら、ショートカットを取ることを含めて、あなたが慣れている何でもすることができます。

セッターを使用する利点の1つは、コードの1つの場所でのみチェックを実行する必要があることです。

これらのメソッドによって実際に取得および設定されているものに注意を払うことをお勧めします。これらを使用して定数値へのアクセスを提供している場合は、おそらく定数を使用する方が良いでしょう。

4

これは、問題のプログラミング言語に依存します。あなたの質問はJavaのコンテキストで組み立てられており、ゲッターとセッターは一般に良いものと考えられているようです。

対照的に、Pythonの世界では、それらは一般に悪いスタイルと見なされます。機能を実際に追加せずにコードに行を追加します。 Pythonプログラマが必要な場合、メタプログラミングを使用して、オブジェクト属性の取得や設定をキャッチできます。

Java(少なくともJavaのバージョンでは、10年前に少し学んだ)では、それは不可能でした。したがって、Javaでは、通常、ゲッターとセッターを宗教的に使用するのが最善です。必要に応じて、変数へのアクセスをオーバーライドできます。

(これはPythonを必ずしもJavaよりも優れたものにするわけではなく、単に異なるだけです。)

3
user25148

参考までに:このスレッドのすべての優れた回答に加えて、ゲッター/セッターの賛成または反対の理由として考えられるのは、パフォーマンスが1つではないことです(一部の人は信じているかもしれません)。 JVMは、ささいなゲッター/セッター(実際にオーバーライドされない限り、_final以外のものでも)をインライン化するのに十分スマートです。

2
gustafc

クラスの一部を値クラスに置き換えることができます。これにより、ゲッターを削除し、コンテンツが自分の下から変更された場合の問題を回避できます。

1
David Segonds

フィールドの個々の値への外部アクセスが必要な場合は、ゲッターおよび/またはセッターを使用します。そうでない場合は、しないでください。パブリックフィールドを使用しないでください。それはそれと同じくらい簡単です! (OK、決してthat simpleではありませんが、大まかな目安です)。

一般に、特にオブジェクトを不変にしようとする場合は、ゲッターよりもはるかに少ないセッターを提供する必要があることにも気付くはずです。

1
philsquared

私は数か月前にJavaでプログラミングを行ってきましたが、アプリケーションに必要な場合にのみゲッターとセッターを使用する必要があることを学びました

楽しんで :)

0
Giancarlo