Getter & Setter:使用还是废弃

  • 私有变量
    • 为什么我们要使用私有的实例变量呢?
      • 因为我们不希望其他类直接的依赖于这些变量。而且在心血来潮时,我们还可以灵活的修改变量类型和实现。
      • 然而,为什么程序员们都自动在对象中加入getter和setter方法,以此对外暴露私有变量,就如同这些变量是公有的一样?
  • 存取方法
    • 存取方法(又被称为getters和setters)是一些可以用来读写对象实例变量值的方法。
  • 为什么使用存取方法?
    • 在类中使用存取方法而非直接暴露属性是有理由的。
    • Getter和Setter使得API更加的稳定。
      • 比如,假设类中有一个公共属性,它可以被其他类直接存取。一段时间后,你想要在读取或保存这个公共属性的时候添加额外的逻辑。这将影响到已经使用这个API的类。所以对这个公共属性的任何改变都会导致引用这个属性的其他类的改变。
      • 相反,使用存取方法,我们可以随后很容易的添加其他的一些逻辑,比如缓存数据,延迟加载。而且,如果新的属性值与旧的属性值不同,我们还可以触发属性改变事件。
      • 所有这些对于通过使用存取方法获取值的类来说都是透明的。
  • 是否要对所有的属性都是使用存取方法?
    • 属性可以被声明为包级私有或是私有嵌套类可见。在这些类中,相对于使用存取方法而言,对外直接暴露属性字段可以减少类定义和调用代码中的视觉混乱。
    • 如果一个类是包级私有或是私有嵌套类可见,假设它的属性字段很好的描述了类所提供的数据,那么对外暴露这些属性字段本质上是没有问题的。
    • 这样的类被限制在类所声明的包内,同时调用代码受限于类内部表示。我们可以修改这个类,而不用改变任何包外的代码。而且,对于私有嵌套类,改动的范围进一步的被缩小到被嵌套类里。
    • 使用公共属性的另一个例子是JavaSpace 请求对象。Ken Arnold讲述了他们决定使用公共属性,而不是带存取方法的私有属性的经历(详情)
    • 人们被告知不要使用公共属性,公共属性不好,有时这会让人们感觉不舒服,而且时常人们会使用不容置疑的语气来论述。但是我们不是非常虔诚的那些人。制定规则是有理由的。对于私有属性规则的理由并不适用于这个特例。这是一个特殊的例外,我也告诉人们不要在他们的类中使用公共属性,但也存在例外。这就是这个规则的一个例外,因为仅仅说它是一个属性会更加简单和安全。我们退一步想一想:既然这样,为什么要这条规则呢?它是否适用呢?在这个例外中,它并不适用。
  • 私有属性 + 公共存取方法 == 封装?
    • 考虑下面的例子
    • 我们通常都认为以上是糟糕的代码风格,因为它破坏了封装性。替代方法是:
    • 有人认为这样封装了属性。这真的实现了封装吗?
      • 实际上,Getter/Setter和封装性没有任何关系。
      • 数据并没有比使用公共属性获得更多隐蔽或封装。
      • 其他的类对这个类的内部细节仍然了如指掌。
      • 类的改动可能会蔓延,迫使依赖它的其他类做出相应的修改。以这种方式使用的Getter和Setter通常破坏了封装性。
      • 一个真正完整封装的类是没有setter方法的,而且最好也没有getter方法。类应该负责使用自身的数据计算并返回结果,而不是从某个类获得数据并计算这些数据。
    • 看下面的例子
    • 如果我们需要获得一个特殊的页面,我们会编写以下的代码,
    • 这里值得注意的是:
      • 而且,更糟糕的是Map的任何客户端代码都可以清空这个Map,这通常是我们所不希望的。
      • 客户端代码需要从Map里获得一个对象并把它转换为合适的类型。
    • 相同逻辑的替代实现方法是:
    • 这样隐藏了Map实例和交互接口(Map)。
  • Getters和Setters的过度使用
    • 创建私有属性,随后通过IDE自动生成所有这些属性的getters和setters方法,这和直接使用公共属性是一样的糟糕。
    • 过度使用的一个原因是现在在IDE中仅仅需要使用几个点击事件就可以创建这些存取方法。这些完全无意义的getter/setter代码有时会比类的逻辑代码本身还要长,你会多次阅读这些代码,虽然你并不想这么做。
    • 所有的属性都应该保持私有,但对不可改变的属性仅仅增加setter方法。增加一个不必要的getter会暴露内部结构,这也增加了代码耦合的机会。避免方案是在每次增加存取方法的时候,我们应该分析是否可以通过封装行为来替代存取方法。
    • 让我们看看另一个例子
    • 依据以上的逻辑,假设我们随后认为数据类型double不够合适,而是应该使用BigDecimal,这样那些已经使用了这个类的客户端代码也会失效。
    • 让我们重建上面的例子
    • 与之前直接请求数据不同,类负责增加它自己的值。使用这种方式,将来任何改变数据类型的请求都不需要改变任何客户端代码。这样,不仅仅封装了数据,而且也封装了数据的保存方式甚至数据是否存在的事实。
  • 结论
    • 通过使用存取方法来限制对属性变量的访问要优于直接使用公共属性变量。
    • 但是,为每一个属性都创建getter和setter方法确实有些极端。而且这也要根据具体的情况来定,有些时候你仅仅希望有一个单纯的数据对象而已。应该为真正需要的属性添加存取方法。
    • 一个类应该使用它自身的属性,并对外提供强大的功能,而不是仅仅作为一个被其他类操作的存储状态属性的存储池。

原文发布于微信公众号 - java思维导图(java-mindmap)

原文发表时间:2017-12-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jimoer

Java设计模式学习记录-模板方法模式

模板方法模式,定义一个操作中算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

1704
来自专栏精讲JAVA

优化 Java 中的多态代码

Oracle的Java是一个门快速的语言,有时候它可以和C++一样快。编写Java代码时,我们通常使用接口、继承或者包装类(wrapper class)来实现多...

1192
来自专栏黑泽君的专栏

Eclipse保存文件时出现字符编码错误

eclipse 由于开源所以支持了比较杂的编码方式,而这些一个工程导入时添加了不少的外来程序,由于不是同一工程一次编码带来了其中含有 GBK 或 UTF8 或 ...

2811
来自专栏Java架构师学习

十年Java”老兵“浅谈源码的七大设计模式

一个专业的程序员,总是把代码的清晰性,兼容性,可移植性放在很重要的位置。他们总是通过定义大量的宏,来增强代码的清晰度和可读性,而又不增加编译后的代码长度和代码...

37312
来自专栏喔家ArchiSelf

一个函数的自白

我是——编程世界的函数,不是数学中的幂,指,对和三角函数等等,但是和f(x)又有着千丝万缕的关系。

1295
来自专栏CDA数据分析师

教你一招:用 50 行 Python 代码制作一个计算器

简介 在这篇文章中,我将向大家演示怎样向一个通用计算器一样解析并计算一个四则运算表达式。当我们结束的时候,我们将得到一个可以处理诸如 1+2*-(-3+2)/5...

2047
来自专栏编程

PLC编程优化方法,让程序运行提速!

PLC、DCS、仪器仪表、电气技术资料,一网打尽 通过本方法优化可以极大的减少程序语句数,使PLC程序更简洁、可读性更好,由于不需要做耗时的类型转换,程序运行效...

2149
来自专栏walterlv - 吕毅的博客

应该抛出什么异常?不应该抛出什么异常?(.NET/C#)

2018-02-04 13:25

1762
来自专栏WindCoder

Java设计模式学习笔记—桥接模式

文章最后“Java设计模式笔记示例代码整合”为本系列代码整合,所有代码均为个人手打并运行测试,不定期更新。本节内容位于其Bridge包(package)中。

881
来自专栏Android先生

Java面向对象OOP

(1)、单一职责原则(Single Responsibility Principle)

752

扫码关注云+社区

领取腾讯云代金券