用途
封装是一项面向对象设计技能,用于在类设计中隐藏内部状态、只暴露行为而非数据,并通过方法控制对状态的访问。资料指出,公开字段会让任何人修改对象内部、绕过校验并破坏不变量,而封装用于保护数据完整性。
何时使用
资料列出以下适用场景:
- 设计带状态的类
- 想图方便把字段设为 public
- 外部代码直接修改对象状态
- 直接访问可能破坏不变量
核心原则
资料提出“铁律”:绝不直接暴露内部状态,始终用方法控制访问。没有例外,不因“更简单”“我们信任调用方”“这只是数据”“getter/setter 太啰嗦”而破例。
检测:暴露状态坏味道
如果内部状态可直接访问,就应停止。资料给出的违规示例中,BankAccount 的 balance 和 transactions 为 public,调用方可以把余额设为负数、清空审计记录。问题包括:变更无校验、不变量容易被破坏、没有审计轨迹、无法更改内部表示。
正确模式:私有状态,公开方法
资料演示将 _balance、_transactions 设为 private,通过 get balance() 只读访问,通过 deposit()、withdraw() 做正数校验与余额校验并记录交易,getTransactionHistory() 返回副本。这样不变量受到保护,非法赋值会报错。
封装技巧
- 私有字段:如
User的_password私有,通过setPassword()校验长度并哈希,通过checkPassword()验证。 - 用 readonly 提供只读访问:如
Config的apiUrl、timeout为 readonly,在构造函数中赋值。 - 返回副本而非引用:
Order的getItems()应返回[...this._items]或ReadonlyArray<OrderItem>,而不是直接返回this._items。 - 在 setter 中校验:如
Product的pricesetter 校验不能为负、不能过高。
压力抵抗协议
资料针对四种常见压力给出回应:
- “更简单”:现在代码少,以后 bug 多;封装防止非法状态。做法是私有字段加方法,多出的代码就是校验。
- “我们信任调用方”:团队会扩大、代码会演进、错误会发生;应在代码中强制保护不变量,而非依赖调用方自律。
- “这只是数据”:数据也有规则,邮箱有格式、年龄有范围;纯数据用 DTO/接口,类等于行为加封装。
- “getter/setter 啰嗦”:TypeScript 有简洁的 get/set 语法,
get balance()并不啰嗦。
红旗清单
资料列出应停止并重新考虑的信号:可变字段带 public 关键字、外部直接属性赋值、按引用返回数组/对象、状态变更无校验、不变量只写在文档而未强制。出现这些就意味着应封装状态。
快速参考
暴露(坏)对封装(好):public balance 对 private _balance 加 deposit()/withdraw();return this.items 对 return [...this.items];直接修改对带校验的方法;信任调用方对在类中强制。
常见合理化借口(均无效)
资料逐条反驳:“更简单”是写起来简单、维护更难;“我们信任调用方”是代码应强制而非信任;“只是数据”是数据有约束应强制;“啰嗦”是 TypeScript getter 很简洁;“过度工程”其实只是工程。
底线
私有状态、公开方法、每次变更都校验。绝不直接暴露内部状态,返回集合副本,在 setter 中校验。封装保护不变量并支持安全演进。
限制
资料未提供该技能的具体安装命令、运行环境要求、收费方式或平台支持信息,暂未提供。