封装
当其他部分的代码想要执行对象的某些操作时,可以借助对象向外部提供的接口完成操作,借此,对象保持了自身的内部状态不会被外部代码随意修改。也就是说,对象的内部状态保持了私有性,而外部代码只能通过对象所提供的接口访问和修改对象的内部状态,不能直接访问和修改对象的内部状态。保持对象内部状态的私有性、明确划分对象的公共接口和内部状态,这些特性称之为封装(encapsulation)。
封装的好处在于,当程序员需要修改一个对象的某个操作时,程序员只需要修改对象对应方法的内部实现即可,而不需要在所有代码中找出该方法的所有实现,并逐一修改。某种意义上来说,封装在对象内部和对象外部设立了一种特别的“防火墙”。
例如,假设学生只有在二年级以后才能学习弓箭课,我们可以将学生的 year 属性暴露给外部,从而外部代码可以通过检查学生的 year 属性来确认该学生是否可以选修该课程。
jsif (student.year > 1) {
// 允许学生选修弓箭课
}
问题在于,如果我们决定修改选修弓箭课的标准(例如需要家长的同意),我们需要在选课系统的代码中修改每一个相关的部分,这是相当麻烦的,并且这一过程中程序员很可能出错。现在,如果我们向 Student 类中添加一个 canStudyArchery() 方法(用于检查学生是否能够选修弓箭课),那么相应代码的实现逻辑就会集中在一个地方:
class Student : extends Person
properties
year
constructor
Student(name, year)
methods
introduceSelf()
canStudyArchery() { return this.year > 1 }
jsif (student.canStudyArchery()) {
// 允许学生选修弓箭课课
}
这样一来,如果我们要修改选修弓箭课的规则,我们只需要更新 Student 类中的相应方法即可,而其他地方的代码无需修改,整个系统仍旧可以正常工作。
在许多面向对象编程语言中,我们可以使用 private 关键字标记对象的私有部分,也就是外部代码无法直接访问的部分。如果一个属性在被标记为 private 的情况下,外部代码依旧尝试访问该属性,那么通常来说,计算机会抛出一个错误。
class Student : extends Person
properties
private year
constructor
Student(name, year)
methods
introduceSelf()
canStudyArchery() { return this.year > 1 }
student = new Student('Weber', 1)
student.year // 错误:'year'是学生类的私有属性
也有部分语言并不采用强制措施阻止外部代码访问对象的私有属性,在这种情况下,程序员们通常会采用一些约定俗称的命名方式来标记对象的私有部分,例如将以下划线开头的变量名看作是对象的私有部分。