抽象与封装总是分不清?这篇文章帮你一次理清
学习面向对象(OOP)时,总会遇到一个必须跨越的难关。
那就是抽象与封装。
这两个概念很容易听起来几乎一样。面试中被问到“抽象和封装有什么区别?”时,也很容易含糊回答。
先说结论。
抽象是决定“展示什么”的设计视角,而封装是决定“如何隐藏”的实现视角。
掌握这句话,就已经解决了一半。接下来用示例把剩下的内容彻底巩固下来。
什么是抽象?
抽象是隐藏复杂的内部实现,只提取并展示必要的本质。
想想汽车。开车时,我们不需要知道发动机如何让燃料爆炸。
方向盘、踏板、挡位。只要了解这个接口,就能开车。
也就是说,思考向驾驶员暴露什么,就是抽象。
用代码可以这样表示。
protocol Coffee {
func brew()
}
struct Americano: Coffee {
func brew() {
print("冲煮美式咖啡")
}
}
let menu: Coffee = Americano()
menu.brew()
// 输出:冲煮美式咖啡
调用方只知道本质brew()。至于如何研磨咖啡豆、将水调到多少度,都不在关注范围内。
这就是抽象。
什么是封装?
封装是将数据与处理这些数据的功能绑定在一起,并阻止外部直接访问。
核心是隐藏和保护。
想想银行账户。如果外部可以随意修改余额,后果会很严重。
因此要隐藏余额,只允许通过存款、取款等规定的渠道进行修改。
class Account {
private var balance = 0
func deposit(_ amount: Int) {
guard amount > 0 else { return }
balance += amount
}
func getBalance() -> Int {
return balance
}
}
let acc = Account()
acc.deposit(1000)
print(acc.getBalance())
// 输出: 1000
我们用private阻止了balance。外部只能通过规定的入口deposit修改值。
像这样隐藏内部实现并控制访问,就是封装。
那么,两者的决定性区别是什么?
总结如下。
| 区分 | 抽象 | 封装 |
|---|---|---|
| 目的 | 隐藏复杂性,只暴露本质 | 隐藏并保护数据 |
| 视角 | 设计视角(是什么) | 实现视角(怎么做) |
| 问题 | “应该展示什么?” | “应该如何隐藏?” |
| 手段 | 接口、抽象类 | 访问控制修饰符(如 private) |
下面指出最容易混淆的地方。
两者都涉及隐藏某些东西,所以容易混淆。
但它们隐藏的对象不同。
抽象隐藏复杂流程(逻辑),让使用变得简单;而封装隐藏数据(状态),以便安全保护。
什么时候该关注哪一个?
实际工作中,两者不是分开使用,而是一起使用,只是关注点不同。
- 设计新功能的接口时 → 关注抽象
- 需要保证类内部状态的完整性时 → 关注封装
- 协作时思考要向他人暴露的 API → 抽象
- 安全管理值、避免 bug 时 → 封装
面试时会这样问
Q. 请解释抽象和封装的区别。
关键在于视角不同。抽象是隐藏不必要的细节、只展现本质的设计概念;封装则是隐藏数据、限制访问的实现概念。再补充说明抽象通过接口实现,封装通过访问控制修饰符实现,会更完整。
Q. 为什么需要封装?
如果外部直接修改对象状态,数据完整性很容易被破坏。让状态只能通过规定的方法修改,就能将验证逻辑集中在一处,也更容易维护和调试。
这两个概念不是对立的,而是一对搭档。如果今天的示例让你理解了它们,下次写代码时只要想一次:“我现在展示了什么,又隐藏了什么?”OOP 就会清晰得多。

