本篇记录多种重构技巧,旨在隐藏数据结构的实现细节、管理类之间的依赖关系以及确保代码的清晰和可维护性
封装是改进软件设计和消除 “纯数据类”(Data Class)坏味道的关键
数据结构的封装技巧
这些技巧专注于隐藏和管理不同类型的数据结构,如记录、集合和基本类型。
封装技巧(中文/英文) | 动机(Why) | 做法/说明 | 示例 |
封装记录 (Encapsulate Record) | 隐藏记录型结构的内部细节,让用户不必追究存储和计算过程。有助于字段改名,可渐进修改调用方。 | 将记录型结构包装进一个类中。为字段提供访问函数(取值/设值),隐藏结构细节。对于嵌套结构,重点处理更新操作,读取操作可返回副本或只读代理。 | 组织信息 organization = { name: "...", country: "..." } 被封装进 class Organization 中,通过 getName() 和 setCountry() 等方法访问。 |
封装集合 (Encapsulate Collection) | 限制对集合的直接修改,防止客户端的意外修改破坏封装的集合。 | 永远不直接返回集合的字段。添加专门用于 添加集合元素 和 移除集合元素 的函数。修改取值函数,使其返回 只读代理 或 数据副本。 | 避免直接访问 aCustomer.orders,改为通过 aCustomer.numberOfOrders 或专门的添加/移除函数来操作。 |
以对象取代基本类型 (Replace Primitive with Object) | 大量使用基本类型(如字符串、整数)来表示领域概念(如钱、电话号码)会导致“基本类型偏执”的坏味道。 | 创建一个新类(值对象),用于封装原始基本类型的数据值和相关行为。这个新类可以容纳验证和比较逻辑。 | 将表示订单优先级的字符串替换为 class Priority 对象,该对象可以封装验证逻辑和比较逻辑。 |
以查询取代临时变量 (Replace Temp with Query) | 消除只计算一次且之后不再修改的临时变量,简化函数分解过程,避免向新提炼函数传递变量作为参数。 | 将为临时变量赋值的代码段提炼成一个 函数(查询函数),该函数没有副作用。然后用 内联变量 移除临时变量。 | 将计算 basePrice 的临时变量 basePrice = this._quantity * this._itemPrice 替换为一个 getBasePrice() 的函数。 |
模块和类的组织技巧
这些技巧关注于类的拆分、合并以及对类的接口和依赖关系的调整。
封装技巧(中文/英文) | 动机(Why) | 做法/说明 | 示例(来自来源,以概念阐释为主) |
提炼类 (Extract Class) | 当一个类承担了 过多责任(过大的类)时,将彼此相关的变量和行为分离到新的类中。 | 识别并创建一个新类来承担分离出的责任。运用 搬移字段 和 搬移函数 将相关特性移至新类,并在旧类中建立对新类的引用。 | 将 Person 类中与电话号码相关的字段和函数提炼到 TelephoneNumber 类中。 |
内联类 (Inline Class) | 如果一个类不再带来足够的结构或价值(冗赘的元素),或者其大部分功能都被转移到其他地方后,它已沦为一个 空壳。 | 将源类中的函数与数据全部搬移到目标类,然后删除源类。 | 将 TrackingInformation 类中的物流追踪信息内联到 Shipment 类中,因为前者只为后者提供一个简单的封装。 |
委托和依赖的封装技巧
这些技巧关注于管理对象间的协作,避免不必要的耦合。
封装技巧(中文/英文) | 动机(Why) | 做法/说明 | 示例(来自来源,以概念阐释为主) |
隐藏委托关系 (Hide Delegate) | 隐藏对象的内部结构,避免客户端代码与查找过程中的 导航结构紧密耦合(过长的消息链)。 | 在宿主对象上创建委托函数,将客户端对被委托对象的调用转发给宿主对象。 | 客户端不应直接调用 person.department.manager,而应通过 person.manager,由 person 隐藏对 department 的委托。 |
移除中间人 (Remove Middle Man) | 如果一个类的接口一半函数都委托给其他类,造成过度运用委托(中间人),应直接和真正负责的对象打交道。 | 如果中间人函数很少,使用 内联函数;否则,修改客户端直接调用委托对象。 | 如果 person.manager 只是简单调用 person.department.manager,则移除 person 上的 manager 方法,让客户端直接访问 person.department.manager。 |
替换算法 (Substitute Algorithm) | 当发现做一件事有 更清晰、更简单 的方式时,应取代复杂、难以理解的算法。 | 确保原算法已被提炼到独立函数中。编写新算法并将其写入函数体,通过测试确保外部行为不变。 | 将一段复杂的 for 循环和 if/else 逻辑替换为更简洁的数组 candidates 和 find 方法。 |
总结
封装技巧主要服务于 改进软件的设计,特别是通过 解耦 和 信息隐藏 来对抗设计腐败。通过这些重构手法,数据和行为被合理地组织起来,使得 软件更容易理解,并且 更易于修改,从而从根本上 提高编程速度。
封装技巧的作用就像是把一个复杂的机械手表(软件系统)的内部零件用一个个独立、有盖子的模块(类、记录)包装起来。你不需要知道秒针是怎么转动的(内部实现细节),只需要通过外壳上的接口(封装函数)来设置时间或读取信息。如果某个零件(例如电话号码)需要升级或修改,你只需打开它自己的模块盖子,而不会不小心破坏了相邻的零件(其他类的逻辑)。
- Author:CoderWdd
- URL:https://www.wuinsights.top//article/622b2cab-939f-4509-811f-ed1a55e93e8b
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts