如何避免吸气剂和二传手

Nag*_*ran 25 oop ooad design-patterns properties accessor

我在很多地方都读过"吸气鬼和恶魔都是邪恶的".我理解为什么这样.但我不知道如何完全避免它们.Say Item是一个包含项目名称,数量,价格等信息的类......而ItemList是一个类,它有一个Items列表.要找到总计:

int grandTotal()
{
int total = 0;

for (Item item: itemList)
       total += item.getPrice();

return total;
}

在上面的例子中,如何避免getPrice()?Item类提供了getName,setName等....

我该如何避免它们?

Jon*_*and 41

什么时候应该使用吸气剂和二传手?

getter和setter非常适合配置或确定类的配置,或从模型中检索数据

获得物品的价格是一个完全合理的使用吸气剂.这是需要可用的数据,可能需要特别考虑,通过向设置器添加验证或清理来保护数据.

你也可以提供没有setter的getter.他们没有必须成对出现.

什么时候不应该使用getter和setter?

有时,对象依赖于永远不会暴露的内部属性.例如,迭代器和内部集合.公开内部收集可能会产生极大的负面和意外后果.

另外,例如,假设您正在通过某些HttpURLConnection进行通信.为您的HttpURLConnection公开setter意味着如果在等待接收数据时更改连接,您可能会得到非常奇怪的状态.此连接应该在实例化时创建或在内部完全管理.

摘要

如果您拥有公开的所有意图和目的的数据,但需要进行管理:使用getter和setter.

如果您有需要检索的数据但在任何情况下都不应该更改:使用getter而不是setter.

如果您有需要为内部目的设置的数据且不应公开公开(并且不能在实例化时设置):使用setter而不是getter(setter可能会阻止第二次调用影响内部属性)

如果你有一些完全是内部的东西,没有其他类需要访问它或直接更改它,那么两者都不使用.

不要忘记,setter和getter可以是私有的,甚至对于内部管理的属性也是如此,拥有管理属性的setter可能是可取的.例如,获取连接字符串并将其传递给HttpURLConnection的setter.

另请注意:

Allen Holub的文章为什么getter和setter方法是邪恶的似乎是OP推理的来源,但在我看来,这篇文章很难解释它的观点.

编辑:添加摘要
编辑2:拼写更正


Fin*_*las 19

看到一个小小的声音少数人反对整个" 吸气者和塞特犬 "的辩护是邪恶的辩论,这是一种耻辱.首先,文章标题是故意挑衅性的吸引你,就像任何博客文章一样.我之前在博客上发表过关于此的博客,几年之后更新了我对这个问题的看法和想法.我会在这里总结一下我能做的最好的事情.

  • 吸毒者和二传手(访问者)并不邪恶
  • 然而,它们在大多数时候都是"邪恶的"(不必要的)
  • 封装不仅仅是在私有字段周围添加访问器来控制更改,毕竟添加只需修改私有字段的get/set方法没有任何好处
  • 您应该使用" 告诉,不要问 " 的原则编写尽可能多的代码
  • 需要使用访问器来进行框架代码,DTO,序列化等.不要试图打这个.
  • 您希望您的核心域逻辑(业务对象)尽可能不受属性限制.你应该告诉对象做事情,而不是随意检查他们的内部状态.

如果你有一堆访问器,你基本上违反了封装.例如:

class Employee
{
   public decimal Salary { get; set; }

   // Methods with behaviour...
}
Run Code Online (Sandbox Code Playgroud)

这是一个垃圾域对象,因为我可以这样做:

me.Salary = 100000000.00;
Run Code Online (Sandbox Code Playgroud)

这可能只是一个简单的例子,但任何在专业环境中工作的人都可以证明,如果有一些公共人员会使用它.开发人员看到这一点并开始使用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
}
Run Code Online (Sandbox Code Playgroud)

现在我们不能依赖薪酬作为公共知识.任何想要给员工加薪的人都必须通过这种方法来做到这一点.这很好,因为它的业务逻辑包含在一个地方.我们可以在使用Employee的每个地方更改这个地方并生效.

  • @JonathanFingland是的,它不一样.我的观点是要证明你如何增加/减少员工的工资.__NOT__获得/设定他们的薪水.根据员工管理域任意设置员工薪酬是没有意义的. (2认同)

fic*_*ion 14

以下示例是样板定位器和吸气剂的绝佳示例.

class Item{
  private double price;

  public void setPrice(final double price){
    this.price = price;
  }
  public double getPrice(){
    return this.price;
  }
}
Run Code Online (Sandbox Code Playgroud)

一些程序员认为这称为封装,但事实上这段代码完全相同

class Item{
  public double price;
}
Run Code Online (Sandbox Code Playgroud)

在这两个类中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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

这是一个真正的封装,类的不变量是由守护的setPrice.我的建议 - 不要编写虚拟的getter和setter,只有在他们保护你的类的不变量时才使用getter和setter

  • 一个问题是,您在编写本文时假设您知道要执行的所有验证和错误检查.编写setter的简单案例的关键是你可以添加参数转换/验证/等.稍后不改变对*someitem*.setPrice(*price*)的调用.添加异常显然会产生连锁效应,但无论如何都是如此. (8认同)
  • @fiction:您无法在几秒钟内更改其他人使用您的课程的代码. (3认同)
  • @Jonathan使用现代IDE,重命名或创建getter/setter只需几秒钟.并且getter/setter允许更精确的调试.我的观点是虚拟getter/setter使得代码更难阅读. (2认同)

jas*_*son 7

我在很多地方都读过"吸气鬼和恶魔都是邪恶的".

真?这听起来很疯狂.许多?告诉我们一个.我们会撕成碎片.

我理解为什么这样.

我不.这对我来说似乎很疯狂.要么你误解了,要么认为你明白了,或者原始来源只是疯了.

但我不知道如何完全避免它们.

你不应该.

怎么避免getPrice

看,你为什么要避免这种情况?你怎么想从你的对象中获取数据呢?

怎么避免他们?

别.停止阅读疯狂的谈话.

  • 无论编程语言如何,@ Jason Exposing对象内部都是糟糕的OO风格.并且你必须提供一个高质量的答案,你没有 - 我不是因为我不同意而贬低你,而是因为你没有费心去研究这个问题并将其视为"疯狂的谈话".一个经过充分研究的答案会注意到设计问题,并解释了为什么在某些情况下破坏封装是不可避免的. (7认同)
  • -1因为没有努力去理解这个问题.事实上,Allen Holub的文章是规范的来源,但这不是疯狂的谈话.这是一个有效的问题.我们被教导盲目地写大量的getter和setter并不能使它成为编写Java代码的最佳方式 (5认同)
  • [这](http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html)似乎是规范的文章,但是,这是疯狂的谈话. (3认同)
  • @Andres F.:Pardon?1. OP提供上下文; 我在答案中告诉OP提供上下文,以便我们可以剖析它.2. OP未标记为Java.OP没有链接到Holub的文章,因为没有阅读它而贬低某人是asinine; 如果可以的话,我会拒绝你的评论.没有人明智地盲目写作吸气剂和制定者. (3认同)
  • 完全同意.欲了解更多信息,请访问此链接查看LBushkin的答案http://stackoverflow.com/questions/1568091/why-use-getters-and-setters (2认同)
  • @AndresF.虽然这篇文章提出了一些好的观点,但它充满了顶级的断言,令人难以置信的啰嗦.他说"你不应该使用访问者方法(getter和setter),除非绝对必要"在最后一页.他是对的.他没有提供很好的例子,这使他的观点明显不那么明确.他的中心观点是,您需要考虑课程的用途,并尽可能详细地介绍内部工作.但糟糕的写作会导致像OP这样的案件,这些案件只会吓到吸气者和制定者 (2认同)
  • @Jason我赞成Holub的文章的一点是,类有时会有他们追踪的变量但只能用于内部使用而且永远不需要暴露(例如迭代器及其内部集合).对于那些情况,吸气剂或定位器是不合需要的.Holub只是做了一个很糟糕的解释. (2认同)
  • @Jason哦,我绝对同意,因为我在这里的评论表明.霍尔布的文章有一个很好的观点,但是用煽动性的语言和abolutes来表达,然后他通过与绝对相矛盾来结束.基本上,这篇文章写得不好,而且标题很糟糕.最好说:*有些情况下应该避免吸气剂和固定剂.这里有一些例子"......",仔细考虑你的课程中需要哪些getter和setter,而不是简单地添加它们是为了完整.* (2认同)
  • 然而另一个人误解了"Getters and Setters"是邪恶的观点. (2认同)
  • 评论就足够了."这是疯狂的谈话.不要听疯狂的人,你读疯狂的文章".不是真正的答案. (2认同)

Arn*_*tta 5

当有人告诉你 getter 和 setter 是邪恶的时,想想他们为什么这么说。

吸气剂

他们邪恶吗?代码中没有邪恶这样的东西。代码就是代码,没有好坏之分。这只是阅读和调试的难易程度的问题。

在您的情况下,我认为使用 getter 来计算最终价格是完全可以的。

恶魔”

用例:您认为您在购买商品时想要了解商品的价格。

人们有时会像这样使用 getter:

if(item.getPrice() <= my_balance) {
   myBank.buyItem(item);
}
Run Code Online (Sandbox Code Playgroud)

这段代码没有任何问题,但它并不像它可能的那样直接。看看这个(更实用的方法):

myBank.buyItem(item); //throws NotEnoughBalanceException
Run Code Online (Sandbox Code Playgroud)

在购买商品时检查商品的价格不是买家或收银员的工作。这实际上是银行的工作。假设客户A有一个SimpleBank.java

public class SimpleBank implements Transaction {

  public void buyItem(Item item){
     if(getCustomer().getBalance() >= item.getPrice()){
        transactionId = doTransaction(item.getPrice());
        sendTransactionOK(transactionId);
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

第一种方法在这里似乎很好。但是如果客户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);
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

在使用第一种方法时,您可能认为您是在防御,但实际上您是在限制系统的功能。

结论

也不要请求许可item.getPrice()NotEnoughBalanceException而是请求原谅。