xiaomingVTEX
2021-06-08 17:22:35 +08:00
## 摘录:14 | 面向对象之封装:怎样的封装才算是高内聚?
封装的重点在于对象提供了哪些行为,而不是有哪些数据。也就是说,即便我们把对象理解成数据加函数,数据和函数也不是对等的地位。函数是接口,而数据是内部的实现,正如我们一直说的那样,接口是稳定的,实现是易变的。
理解了这一点,我们来看一个很多人都有的日常编程习惯。他们编写一个类的方法是,把这个类有哪些字段写出来,然后,生成一大堆 getter 和 setter,将这些字段的访问暴露出去。这种做法的错误就在于把数据当成了设计的核心,这一堆的 getter 和 setter,就等于把实现细节暴露了出去。
一个正确的做法应该是,我们设计一个类,先要考虑其对象应该提供哪些行为。然后,我们根据这些行为提供对应的方法,最后才是考虑实现这些方法要有哪些字段。
封装的重点在于对象提供了哪些行为,而不是有哪些数据。也就是说,即便我们把对象理解成数据加函数,数据和函数也不是对等的地位。函数是接口,而数据是内部的实现,正如我们一直说的那样,接口是稳定的,实现是易变的。理解了这一点,我们来看一个很多人都有的日常编程习惯。他们编写一个类的方法是,把这个类有哪些字段写出来,然后,生成一大堆 getter 和 setter,将这些字段的访问暴露出去。这种做法的错误就在于把数据当成了设计的核心,这一堆的 getter 和 setter,就等于把实现细节暴露了出去。一个正确的做法应该是,我们设计一个类,先要考虑其对象应该提供哪些行为。然后,我们根据这些行为提供对应的方法,最后才是考虑实现这些方法要有哪些字段。
```
class User {
private String username;
private String password;
...
// 修改密码
public void setPassword(final String password) {
this.password = password;
}
}
```
但我们鼓励的做法是,把意图表现出来:
```
class User {
private String username;
private String password;
...
// 修改密码
public void changePassword(final String password) {
this.password = password;
}
}
```
这两段代码相比,只是修改密码的方法名变了,但二者更重要的差异是,一个在说做什么,一个在说怎么做。将意图与实现分离开来,这是一个优秀设计必须要考虑的问题。
不过,在真实的项目中,有时确实需要暴露一些数据,所以,等到你确实需要暴露的时候,再去写 getter 也不迟,你一定要问问自己为什么要加 getter 。至于 setter,首先,大概率是你用错了名字,应该用一个表示意图的名字;其次,setter 通常意味着修改,这是我们不鼓励的。
## 评论区
* 沧浪之水:
至于平时说的一些 POJO 的对象,可以看成是数据载体,是可以加 getter,setter 的(没有这些默认的 getter,setter,很多第三方的数据转化都很不方便,比如 json,sql 等)。在使用的时候,不归结为对象就可以了。
* 作者回复: 能分清楚面向对象和 Java 语言,这就是一个很好的区分。能分清楚传输数据和业务对象,这就是一个很好的区分。