我在很多地方读到“getter 和 setter 是邪恶的”。我明白为什么会这样。但我不知道如何完全避免它们。说 Item 是一个包含项目名称、数量、价格等信息的类……而 ItemList 是一个包含项目列表的类。要找到总计:
整数总计() { 整数 = 0; 对于(项目项目:itemList) 总计 += item.getPrice(); 返回总计; }
在上述情况下,如何避免 getPrice()?Item 类提供 getName、setName 等......
我该如何避免它们?
我在很多地方读到“getter 和 setter 是邪恶的”。我明白为什么会这样。但我不知道如何完全避免它们。说 Item 是一个包含项目名称、数量、价格等信息的类……而 ItemList 是一个包含项目列表的类。要找到总计:
整数总计() { 整数 = 0; 对于(项目项目:itemList) 总计 += item.getPrice(); 返回总计; }
在上述情况下,如何避免 getPrice()?Item 类提供 getName、setName 等......
我该如何避免它们?
Getter 和 setter 非常适合配置或确定类的配置,或从模型中检索数据
获取物品的价格是对 getter 的完全合理的使用。那是需要可用的数据,并且可能涉及通过向设置器添加验证或清理来保护数据的特殊考虑。
您还可以提供不带 setter 的 getter。他们不必成对出现。
有时对象依赖于永远不会暴露的内部属性。例如,迭代器和内部集合。暴露内部收藏品可能会产生巨大的负面和意想不到的后果。
另外,例如,假设您正在通过一些 HttpURLConnection 进行通信。为您的 HttpURLConnection 公开设置器意味着如果在等待接收数据时更改连接,您最终可能会遇到非常奇怪的状态。这种连接应该在实例化时创建或完全在内部管理。
如果您有用于所有意图和目的的公开数据,但需要进行管理:使用 getter 和 setter。
如果您有需要检索的数据,但在任何情况下都不应该更改:使用 getter 而不是 setter。
如果您有需要为内部目的设置的数据并且不应该公开暴露(并且不能在实例化时设置):使用 setter 而不是 getter(setter 可能会阻止第二次调用影响内部属性)
如果您有完全内部的东西并且没有其他类需要直接访问或更改它,那么两者都不要使用。
不要忘记 setter 和 getter 可以是私有的,甚至对于内部管理的属性,拥有一个管理属性的 setter 可能是可取的。例如,获取一个连接字符串并将其传递给 HttpURLConnection 的设置器。
另请注意:
Allen Holub 的文章Why getter and setter methods are evil似乎是 OP 推理的来源,但在我看来,这篇文章在解释其观点方面做得很差。
编辑:添加摘要
编辑2:拼写更正
看到一小部分发声的少数人反对整个“ Getters and Setters ”是邪恶的辩论,这是一种耻辱。首先,文章标题是故意挑衅性地吸引您,任何博客文章也应如此。反过来,我之前已经在博客上写过这个问题,几年后更新了我对这个问题的看法和想法。我会在这里总结最好的。
如果您有大量访问器,那么您基本上违反了封装。例如:
class Employee
{
public decimal Salary { get; set; }
// Methods with behaviour...
}
这是一个垃圾域对象,因为我可以这样做:
me.Salary = 100000000.00;
这可能是一个简单的例子,但是任何在专业环境中工作的人都可以证明,如果有一些公开的代码,人们会使用它。开发人员看到这一点并开始使用 Salary 在代码库周围添加大量检查来决定如何处理 Employee 并没有错。
更好的对象是:
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
}
现在我们不能依赖薪水是公共知识。任何想给员工加薪的人都必须通过这种方法来做到这一点。这很棒,因为它的业务逻辑包含在一个地方。我们可以在使用 Employee 的任何地方更改这一位置和效果。
以下示例是样板设置器和获取器的出色示例。
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
中都没有受到保护或封装,但第二个类更容易阅读。
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
. 我的建议 -不要编写虚拟的 getter 和 setter,只有在保护类的不变量时才使用 getter 和 setter
我在很多地方读到“getter 和 setter 是邪恶的”。
真的吗?这对我来说听起来很疯狂。许多?给我们看一个。我们会把它撕成碎片。
我明白为什么会这样。
我不。这对我来说似乎很疯狂。要么你误解了,但认为你确实理解了,要么原始来源只是疯了。
但我不知道如何完全避免它们。
你不应该。
如何避免
getPrice
?
看,你为什么要避免这种情况?您还想如何从对象中获取数据?
如何避免它们???
不。别再读疯话了。
当有人告诉你 getter 和 setter 是邪恶的,想想他们为什么这么说。
他们是邪恶的吗?代码中没有邪恶之类的东西。代码就是代码,没有好坏之分。这只是阅读和调试的难易程度的问题。
在您的情况下,我认为使用吸气剂来计算最终价格是完全可以的。
用例:您认为在购买某物时想要某件商品的价格。
人们有时会像这样使用 getter:
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;
然而,这通常被认为是不好的风格。类变量通常应该保持私有。
请参阅我对这个问题的评论。
由于对话中使用了贬义词“邪恶”,这已经被经常讨论,甚至可能有点像病毒一样传播开来。当然,有时您需要它们。但问题是正确使用它们。你看,Holub 教授的咆哮不是关于你的代码现在在做什么,而是关于把自己装进盒子里,这样未来的变化是痛苦的,而且容易出错。
事实上,我读过的他所有的书,都是以此为主题的。
该主题如何应用于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;
}
}
这一切都很好——但它仍然是“邪恶的”,因为它可能会在未来给你带来很多悲伤。
悲伤很容易来自这样一个事实,即有一天“价格”可能必须考虑不同的货币(甚至可能是更复杂的易货交易计划)。通过将价格设置为双倍,从现在到“天启”(毕竟我们在说邪恶)之间编写的任何代码都会将价格连接到双倍。
传入Price对象而不是 double 对象要好得多(甚至可能是 Good) 。通过这样做,您可以轻松地对“价格”的含义进行更改,而不会破坏现有接口。
如果您发现自己在简单类型上使用 getter 和 setter,请确保您考虑将来可能对接口进行更改。很有可能你不应该这样做。你在用setName(String name)
吗?您应该考虑setName(IdentityObject id)
甚至setIdentity(IdentityObject id)
在出现其他识别模型(头像、钥匙等)的情况下。当然,您总是可以在任何事情上四处走动setAvatar
,setKey
但是通过在您的方法签名中使用一个对象,您可以更轻松地在将来扩展到可以使用新标识属性而不破坏旧对象的对象。
如何避免 getter 和 setter?设计实际作用于它们持有的数据的类。
无论如何,吸气剂都会对数据撒谎。在Item.getPrice()
示例中,我可以看到我得到了一个int
. 但是价格是美元还是美分?它包括税吗?如果我想知道不同国家或州的价格,我还能使用getPrice()
吗?
是的,这可能超出了系统设计的范围,是的,您最终可能只是从您的方法中返回了一个变量的值,但是通过使用 getter 来宣传该实现细节会削弱您的 API。
到目前为止,这里缺少一个不同的观点:getter 和 setter 邀请违反告诉不问原则!
想象一下你去超市购物。最后,收银员要你的钱。getter/setter方法是:你把钱包交给收银员,收银员清点你钱包里的钱,拿走你欠的钱,然后把钱包还给你。
你在现实中是这样做事的吗?一点也不。在现实世界中,您通常不关心“自治”其他“对象”的内部状态。收银员告诉你:“你的账单是 5.85 美元”。然后你付钱。你如何做到这一点取决于你,收银员唯一想要/需要的就是他从你身边收到这笔钱。
因此:您可以通过考虑行为而不是状态来避免 getter 和 setter 。获取器/设置器从“外部”操纵状态(通过执行avail = purse.getAvailableMoney()
and purse.setAvailableMoney(avail - 5.85)
。相反,您想调用person.makePayment(5.85)
.
如何避免 Java 中的 getter 和 setter?使用龙目岛项目
Cloudanger 的答案是之一,但您还必须意识到,项目列表可能包含许多项目对象,上面有订购数量。
解决方案:在它们之间创建另一个类,将您的项目存储在项目列表中以及为该项目订购的数量(假设该类称为 OrderLine)。
OrderLine 将有 Item 和 qty 作为字段。
之后,在 Item 中编写类似 calculateTotal(int qty) 的代码,返回价格 * 数量。在 OrderLine 中创建一个调用 calculateTotal(qtyOrdered) 的方法
将返回值传递给 itemList。
这样,您就可以避免吸气剂。ItemList 只会知道总价。您的代码应该与您的数据一起使用。询问拥有数据的对象来计算 totalPrice,而不是询问该对象提供原始数据来计算您的 totalPrice。
使用辅助类ShoppingCart
。Item
的方法item.addTo(ShoppingCart cart)
会将价格添加到totalSum
购物车的shoppingCart.addItem(Item item, int price)
如果s 是 s 的项,则从Item
to的依赖ShoppingCart
并不是不利的。Item
ShoppingCart
在Item
s 只为 theShoppingCart
而存在并且Item
类很小的情况下,我更有可能将sItem
作为 的内部类ShoppingCart
,以便ShoppingCart
可以访问项目的私有变量。
尽管设计很不直观,但也有可能让Item
类计算总和 ( item.calculateSum(List<Item> items)
),因为它可以访问其他项目的私有部分而不会破坏封装。
对于其他想知道为什么吸气剂不好的人。考虑getPrice()
返回整数的给定示例。如果您想将其更改BigDecimal
为至少更好的东西或带有货币的自定义货币类型,那么这是不可能的,因为返回类型int
公开了内部类型。
真的吗?我不这么认为。相反,getter 和 setter 可以帮助您保护变量的一致性。
getter 和 setter 的重要性在于为私有属性提供保护,这样它们就不能被直接访问,因此最好创建一个item
包含相应 get 和 set 的属性的类。
getter 和 setter 是邪恶的,因为它们破坏了封装并且可以不必要地暴露对象的内部状态并允许以不应该的方式对其进行修改。下面的文章详细阐述了这个问题:
http://programmer.97things.oreilly.com/wiki/index.php/Encapsulate_Behavior,_not_Just_State
您可以通过 using 避免在地方使用 getter 和 setter,_classname__attributename
因为一旦您将任何属性声明为私有,这就是更改后的新名称。
因此,如果 Item 是具有声明为的私有属性的类__price
然后item.getPrice()
你可以写而不是_Item__price
.
它会正常工作。