私は多くの場所で「ゲッターとセッターは悪だ」と読んだことがあります。そして、私はその理由を理解しました。しかし、私はそれらを完全に回避する方法がわかりません。 Say Itemは、アイテム名、数量、価格などに関する情報を持つクラスであり、ItemListは、アイテムのリストを持つクラスです。総計を見つけるには:
int grandTotal() { int total = 0; for(Item item:itemList) total + = item.getPrice(); return total; }
上記の場合、getPrice()を回避するにはどうすればよいですか? Itemクラスは、getName、setNameなどを提供します。
どうすればそれらを回避できますか?
ゲッターとセッターは、クラスの構成を構成または決定したり、モデルからデータを取得したりするのに最適です。
アイテムの価格を取得することは、ゲッターの完全に合理的な使用法です。これは利用可能である必要があるデータであり、検証またはサニタイズをセッターに追加することによってデータを保護するための特別な考慮事項が含まれる場合があります。
セッターなしでゲッターを提供することもできます。それらはペアで来る必要はありません。
オブジェクトは、公開されることのない内部プロパティに依存している場合があります。たとえば、イテレータと内部コレクション。内部コレクションを公開すると、劇的に否定的で予期しない結果が生じる可能性があります。
また、たとえば、HttpURLConnectionを介して通信しているとします。 HttpURLConnectionのセッターを公開すると、データの受信を待機中に接続が変更された場合に、非常に奇妙な状態になる可能性があります。この接続は、インスタンス化時に作成するか、内部で完全に管理する必要があります。
すべての目的と目的のために公開されているが、管理する必要があるデータがある場合は、ゲッターとセッターを使用します。
取得する必要があるが、いかなる状況でも変更してはならないデータがある場合:セッターではなくゲッターを使用します。
内部目的で設定する必要があり、公開してはならない(インスタンス化時に設定できない)データがある場合:ゲッターではなくセッターを使用します(セッターはおそらく、2回目の呼び出しが内部プロパティに影響を与えるのを防ぎます)
完全に内部的なものがあり、他のクラスがそれにアクセスしたり直接変更したりする必要がない場合は、どちらも使用しないでください。
セッターとゲッターはプライベートにすることができ、内部で管理されているプロパティの場合でも、プロパティを管理するセッターが望ましい場合があることを忘れないでください。たとえば、接続文字列を取得して、それをHttpURLConnectionのセッターに渡します。
注:
Allen Holubの記事 ゲッターとセッターのメソッドが悪である理由 OPの推論の原因のようですが、私の意見では、この記事はそのポイントを説明するのに不十分です。
編集:要約を追加
編集2:スペルの修正
小さな声の少数派が全体に対して反発するのを見るのは残念です " Getters and Setters "は邪悪な議論です。まず、記事のタイトルは、他のブログ投稿と同様に、意図的にあなたを引き込むように挑発します。 私は今度はブログを書きました これについては前にそして 数年後にこの質問についての私の意見と考えを更新しました 。ここで私ができる最善のことを要約します。
アクセサがたくさんある場合は、本質的にカプセル化に違反します。例えば:
class Employee
{
public decimal Salary { get; set; }
// Methods with behaviour...
}
私はこれを行うことができるので、これはがらくたドメインオブジェクトです:
me.Salary = 100000000.00;
これは単純な例かもしれませんが、プロの環境で働く人なら誰でも証明できるように、公開されているコードがあれば、それを利用します。開発者がこれを見て、給与を使用してコードベースの周りに大量のチェックを追加し始め、従業員をどうするかを決定することは間違いではありません。
より良いオブジェクトは次のとおりです。
class Employee
{
private decimal salary;
public void GivePayRise()
{
// Should this employee get a pay rise.
// Apply business logic - get value etc...
// Give raise
}
// More methods with behaviour
}
今、私たちは給与が公の知識であることに依存することはできません。従業員に昇給をしたい人は誰でもこの方法でこれをしなければなりません。このためのビジネスロジックはoneの場所に含まれているため、これは素晴らしいことです。これを1つの場所に変更して、従業員が使用されるすべての場所に影響を与えることができます。
次のサンプルは、ボイラープレートセッターとゲッターの見事な例です。
class Item{
private double price;
public void setPrice(final double price){
this.price = price;
}
public double getPrice(){
return this.price;
}
}
一部のコーダーはこれをカプセル化と呼んでいますが、実際にはこのコードはまったく同等です
class Item{
public double price;
}
どちらのクラスでも、price
は保護もカプセル化もされていませんが、2番目のクラスは読みやすくなっています。
class Item{
private double price;
public void setPrice(final double price){
if(isValidPrice(price))
this.price = price;
else throw new IllegalArgumentException(price+" is not valid!");
}
public double getPrice(){
return this.price;
}
}
これは実際のカプセル化であり、クラスの不変条件はsetPrice
によって保護されています。私のアドバイス--ダミーのゲッターとセッターを書かないでください。クラスの不変条件を保護する場合にのみゲッターとセッターを使用してください
私は多くの場所で「ゲッターとセッターは悪だ」と読んだことがあります。
本当に?それは私にはクレイジーに聞こえます。たくさんの?見せてください。私たちはそれを細かく裂きます。
そして、私はその理由を理解しました。
私はしません。それは私には夢中のようです。あなたの誤解されているがあなたが理解したと思うか、または元の情報源がただ狂っている。
しかし、私はそれらを完全に回避する方法がわかりません。
あなたはすべきではありません。
getPrice
を回避する方法は?
ほら、なぜあなたはそれを避けたいのですか?オブジェクトからデータを取得するために、他にどのように想定していますか?
それらを回避する方法???
しないでください。クレイジートークを読むのをやめなさい。
誰かがゲッターとセッターが悪だとあなたに言うとき、考えてくださいなぜ彼らはそれを言っています。
彼らは悪ですか?コードにはevilのようなものはありません。コードはコードであり、良いことでも悪いことでもありません。読み取りとデバッグがどれほど難しいかが問題です。
あなたの場合、最終的な価格を計算するためにゲッターを使用することはまったく問題ないと思います。
ユースケース:何かを購入するときにアイテムの価格が必要だと思います。
人々は時々このようなゲッターを使用します:
_if(item.getPrice() <= my_balance) {
myBank.buyItem(item);
}
_
このコードには何の問題もありませんが、それほど単純ではありません。これを見てください(より実用的なアプローチ):
_myBank.buyItem(item); //throws NotEnoughBalanceException
_
何かを購入するときにアイテムの価格をチェックするのは、買い手やレジ係の仕事ではありません。それは実際には銀行の仕事です。顧客A
が_SimpleBank.Java
_を持っていると想像してください
_public class SimpleBank implements Transaction {
public void buyItem(Item item){
if(getCustomer().getBalance() >= item.getPrice()){
transactionId = doTransaction(item.getPrice());
sendTransactionOK(transactionId);
}
}
}
_
最初のアプローチはここでは問題ないようです。しかし、顧客B
が_NewAndImprovedBank.Java
_を持っている場合はどうなるでしょうか。
_public class NewAndImprovedBank implements Transaction {
public void buyItem(Item item){
int difference = getCustomer().getBalance() - item.getPrice();
if (difference >= 0) {
transactionId = doTransaction(item.getPrice());
sendTransactionOK(transactionId);
} else if (difference <= getCustomer().getCreditLimit()){
transactionId = doTransactionWithCredit(item.getPrice());
sendTransactionOK(transactionId);
}
}
}
_
最初のアプローチを使用するときは防御的であると思うかもしれませんが、実際にはシステムの機能を制限しています。
許可を求めないでください別名item.getPrice()
、代わりに許しを求めてください別名NotEnoughBalanceException
。
getPrice()は、私が想定しているプライベート変数にアクセスしています。
質問に直接答えるには、価格変数を公開し、次のようにコーディングします(構文は言語、ポインターの使用などによって異なる場合があります)。
total += item.price;
ただし、これは一般的に考えられています悪いスタイル。クラス変数は通常、プライベートのままにする必要があります。
質問に対する私のコメントをご覧ください。
ゲッターとセッターを回避する方法は?保持しているデータに実際に作用する設計クラス。
ゲッターはとにかくデータについてうそをつきます。 Item.getPrice()
の例では、int
を取得していることがわかります。しかし、価格はドルまたはセントですか?税金は含まれていますか?別の国または州の価格を知りたい場合でも、getPrice()
を使用できますか?
はい、これはシステムが実行するように設計されている範囲を超えている可能性があります。はい、メソッドから変数の値を返すだけになる可能性がありますが、ゲッターを使用してその実装の詳細をアドバタイズするとAPIが弱くなります。
これは頻繁に議論されており、ダイアログで使用されている蔑称的な用語「悪」の結果として、おそらく少しバイラルになりました。もちろん、必要なときはあります。しかし、問題はそれらを正しく使用することです。ほら、ホルブ教授の怒りはあなたのコードが何をしているのか今ではなく、自分自身をそのため、将来の変更は苦痛でエラーが発生しやすくなります。
実際、私が彼が読んだものはすべて、これをテーマとして扱っています。
そのテーマはクラスにどのように適用されますかItem?
フィクションのアイテムクラスは次のとおりです。
_class Item{
private double price;
public void setPrice(final double price){
if(isValidPrice(price))
this.price = price;
else throw new IllegalArgumentException(price+" is not valid!");
}
public double getPrice(){
return this.price;
}
}
_
これはすべてうまくいっていますが、将来あなたに多くの悲しみを引き起こす可能性があるという意味で、それはまだ「悪」です。
悲しみは、ある日「価格」が異なる通貨を考慮に入れなければならないかもしれないという事実から来る傾向があります(そしておそらくもっと複雑な物々交換スキーム)。価格を2倍に設定することにより、現在から「黙示録」(結局のところ、私たちは悪を話している)の間に書かれたコードは、価格を2倍に配線します。
Doubleの代わりにPriceオブジェクトを渡す方がはるかに優れています(おそらくGoodですら)。そうすることで、既存のインターフェースを壊すことなく、「価格」が意味するものへの変更を簡単に実装できます。
単純な型でゲッターとセッターを使用していることに気付いた場合は、インターフェースに対する将来の変更の可能性を考慮してください。あるべきではない可能性が非常に高いです。 setName(String name)
を使用していますか?他の識別モデル(アバター、キーなど)が表示される場合は、setName(IdentityObject id)
またはsetIdentity(IdentityObject id)
を検討する必要があります。確かに、いつでもどこでもsetAvatar
とsetKey
を回ることができますが、メソッドシグネチャでオブジェクトを使用することで、将来、新しいIDを使用できるオブジェクトに簡単に拡張できます。プロパティであり、レガシーオブジェクトを壊さないでください。
Javaでゲッターとセッターを回避する方法は?使用 Project Lombok
これまでのところここに欠けている別の見方:ゲッターとセッターは 聞かないでください 原則に違反するように誘います!
あなたがスーパーマーケットで買い物に行くと想像してください。結局、レジ係はあなたからお金を欲しがっています。ゲッター/セッターのアプローチは次のとおりです。あなたはあなたの財布をレジ係に渡し、レジ係はあなたの財布の中のお金を数え、あなたが借りているお金を受け取り、そして財布を返します。
それはあなたが実際に物事を行う方法ですか?どういたしまして。現実の世界では、通常、「自律的な」他の「オブジェクト」の内部状態については気にしません。レジ係はあなたに言います:「あなたの請求書は5,85米ドルです」。それからあなたは支払います。それをどのように行うかはあなた次第です。レジ係が望んでいる/必要としているのは、あなたの側からその金額を受け取ることだけです。
したがって、状態ではなく、動作の観点から考えることで、ゲッターとセッターを回避します。ゲッター/セッターは、「外部」から状態を操作します(avail = purse.getAvailableMoney()
およびpurse.setAvailableMoney(avail - 5.85)
を実行します。代わりに、person.makePayment(5.85)
を呼び出します。
Cloudangerの答えは1つですが、アイテムリストには、数量が注文された多くのアイテムオブジェクトが含まれている可能性があることも理解する必要があります。
解決策:アイテムをアイテムリストに保存し、そのアイテムの注文数量を格納する別のクラスをそれらの間に作成します(クラスがOrderLineと呼ばれるとします)。
OrderLineには、フィールドとしてItemとqtyがあります。
その後、price * qtyを返すItemにcalculateTotal(int qty)のようなコードを記述します。 calculateTotal(qtyOrdered)を呼び出すメソッドをOrderLineに作成します
戻り値をitemListに渡します。
このようにして、ゲッターを回避します。 ItemListは合計金額のみを認識します。あなたのコードはあなたのデータと一緒に生きるべきです。 totalPriceを計算するために生データをオブジェクトに要求するのではなく、データを持っているオブジェクトにtotalPriceを計算するように依頼します。
ゲッターとセッターは、カプセル化を破り、オブジェクトの内部状態を不必要に公開し、本来あるべきではない方法で変更できるため、悪です。次の記事では、この問題について詳しく説明しています。
http://programmer.97things.oreilly.com/wiki/index.php/Encapsulate_Behavior,_not_Just_State
本当に?私はそうは思いません。それどころか、ゲッターとセッターは変数の一貫性を保護するのに役立ちます。
ゲッターとセッターの重要性は、プライベート属性に直接アクセスできないように保護を提供することです。これには、対応するgetとsetを含める属性item
を使用してクラスを作成するのが最善です。
ヘルパークラスShoppingCart
を使用します。 Item
のメソッドitem.addTo(ShoppingCart cart)
は、shoppingCart.addItem(Item item, int price)
を使用してカートのtotalSum
に価格を追加します。
Item
からShoppingCart
への依存関係は、Item
sがShoppingCart
sのアイテムであることが意図されている場合は不利ではありません。
Item
sがShoppingCart
専用に存在し、Item
クラスが小さい場合、Item
を内部クラスとして持つ可能性が高くなります。 ShoppingCart
。これにより、ShoppingCart
はアイテムのプライベート変数にアクセスできるようになります。
非常に直感的ではない設計ですが、カプセル化を壊すことなく他のアイテムのプライベート部分にアクセスできるため、Item
クラスに合計(item.calculateSum(List<Item> items)
)をカウントさせることも可能です。
なぜゲッターが悪いのか疑問に思っている人に。 getPrice()
が整数を返す与えられた例を考えてみましょう。少なくともBigDecimal
のような、または通貨を使用したカスタムマネータイプに変更したい場合は、戻り値の型int
が内部型を公開するため、それは不可能です。