关於封装

什麽是封装

In object-oriented programming (OOP), encapsulation refers to the bundling of data with the methods that operate on that data, or the restricting of direct access to some of an object's components. Encapsulation is used to hide the values or state of a structured data object inside a class, preventing direct access to them by clients in a way that could expose hidden implementation details or violate state invariance maintained by the methods.

封装限制了外部环境存取、操作物件内部属性和方法的能力,另一方面,也隐藏了物件当中方法实作的细节。

限制存取权限

以先前的例子来看,在 BaseballPlayer 类别当中有个属性 name,当建立实例之後,它让外部环境(那些呼叫 jeter.name 的人)可以取得 name 的资料

class BaseballPlayer {
  name: string

  constructor(name: string) {
    this.name = name
  }
}

const jeter = new BaseballPlayer('jeter')
jeter.name            // jeter

然而我们先前没有提到的是,其实外部环境有机会修改这个值:

jeter.name = 'peter'
jeter.name            // peter

如此一来 jeter 变成 peter,就天下大乱了!

所以在物件导向程序语言当中,我们可以透过

  • public
  • private
  • protected

的标注来限制外部环境对於物件内部属性和方法的操作。

譬如以下面的例子来说,我们让 name 属性变为 private,让外部环境无法存取,而同一时间,我们建立了 getName 方法来让外部环境呼叫并取得 name 的值

class BaseballPlayer {
  private name: string

  constructor(name: string) {
    this.name = name
  }

  getName(): string {
    return this.name
  }
}

const jeter = new BaseballPlayer('jeter')
jeter.getName()            // jeter

所以如果有人想用先前同样的方法取得 name 或是修改 name 的话,就会造成错误

jeter.name                 // error
jeter.name = 'peter'       // error

private 让外部环境无法操作物件内部的属性和方法,相反过来,public 就是完全公开。在没有任何标注的情况下,预设所有属性和方法都是公开的。

比较特别的是 protected,它让外部环境无法操作物件内部的属性和方法,但是可以让继承的类别使用 protected 的属性和方法。

隐藏实作细节

以下面这个例子来看,getName 方法将 firstNamelastName 两个属性的资料整合在一起

class BaseballPlayer {
  private firstName: string
  private lastName: string

  constructor(firstName: string, lastName: string) {
    this.firstName = firstName
    this.lastName = lastName
  }

  private getFullName(): string {
    return `${this.firstName} ${this.lastName}`
  }

  getName(): string {
    return this.getFullName().toUpperCase()
  }
}

所以当我们呼叫 getName 的时候,会出现全名。但实际上,外部环境呼叫 getName 的时候,其实可以完全不需要知道这个方法内部是如何运作的,只要 output 的结果如预期就行了。

所以对外部环境来说,实作细节被封装起来了。如果以现实生活的例子来说,当我们投钱进入贩卖机的时候,只期待我们可以拿到想喝的饮料,而不需要去管贩卖机究竟是如何处理硬币、冷藏饮料、选取饮料等等

const jeter = new BaseballPlayer('derek', 'jeter')
jeter.getName()    // DEREK JETER

为什麽要封装

在物件导向程序设计的世界当中,有许许多多的物件,拥有各种不同的属性,用各种不同的方式与这个世界互动。

然而在没有任何限制的情况下,如同最一开始 BaseballPlayer 的例子,jeter 很有可能突然就被改成 peter,一瞬间就认不出在你旁边一起打球的人是谁了,甚至连打球的方式都会不一样了。

而真实世界当中,各种东西的属性和方法本来就无法被任意存取或窜改,每个物件都是有限的,譬如我正在使用的电脑不会突然从 13 寸变成 15 寸,在外面路上跑的公车不会突然就飞上天。

另一方面,封装隐藏了实作细节,让他人可以更快更方便的操作物件。譬如刚刚提到的贩卖机,如果每一个想买饮料的人都先必须知道贩卖机的运作原理的话,那麽这世界其实就会变得很没有效率和麻烦。

封装限制了互动权限,隐藏了实作细节,目的在於提高程序的安全性、稳定性、易用性,并降低错误的发生。


<<:  Day7 - 什麽是Snapshot及如何使用

>>:  [面试]准备好要询问公司的问题,面试就是资讯战!

CSS微动画 - Loading来了!转啊转啊~

Q: 从哪一种Loading开始呢? A: 转圈圈的Loading应该是基本? 之後将会进入一系列...

视觉化当日趋势图(2)-client端设定档&&建立controller、service

今天要来写JAVA的设定档, 首先找到路径:\ShioajiClient\src\main\reso...

伪元素(pseudo element)、伪类别(pseudo element)

伪元素 : Before 、After Before 对指定元素添加最後一个子元素 After 对指...

Day15看鱿鱼游戏就要搭上鱿鱼料理-琉球菜鱿鱼小封

Netflix上的鱿鱼游戏正夯,雪伦也是一集接一集的看完了 上次看机智医生生活搭配辣炒年糕,那这次看...

Day 8. 声明式渲染-跟Vue说Hello

开始Hello Vue前先插播一个错误调试工具- Vue.js devtools 这个chrome浏...