SuperAPI AI 工具集

law-of-demeter

编程与开发

查看项目

更新于 10-16 04:36

技能介绍

本技能介绍并贯彻迪米特法则(Law of Demeter),亦称“不要和陌生人说话”(Don't Talk to Strangers)。其核心原则是:只与自己的直接朋友交谈,不要与陌生人交谈。一个方法只应调用以下对象的方法:自身(this)、其参数、其创建的对象、其直接组件,以及可访问的全局对象(谨慎使用)。绝不应穿透一个对象去访问另一个对象的内部。

适用场景

  • 访问嵌套属性,例如 obj.a.b.c
  • 链式调用方法,例如 obj.getA().getB().getC()
  • 穿透对象获取数据
  • 代码中出现长点号链

核心规则

绝不通过对象链进行访问。要“询问”,不要“伸手去拿”。文中强调没有例外:不因“这样更简单”、不因“只有一条链”、不因“数据就在那里”、不因“代码行数更少”而违反。

违规识别(链式异味)

出现多个点号即构成违规,例如: function getEmployeeCity(company, employeeId) { return company.employees.find(e => e.id === employeeId)?.address.city; } 其他示例包括 user.getProfile().getAddress().getZipCode()、order.getCustomer().getPaymentMethod().getLast4()。

正确模式:询问,而非穿透

让对象直接暴露所需数据。示例中 Employee 类提供 getCity() 返回 this.address.city;Company 类提供 getEmployeeCities() 与 getEmployeeCity(employeeId),内部调用员工的 getCity(),调用方只需 company.getEmployeeCity(employeeId),无需穿透 company 与 employee 的内部结构。

链式调用的危害

  • 紧耦合:调用方需要了解内部结构
  • 代码脆弱:结构变化会破坏所有调用方
  • 隐藏依赖:无法清晰看出真正需要什么
  • 难以测试:必须模拟整条链
  • 空值风险:每个点号都可能是空值

允许的方法调用

类 C 的方法 m 只应调用以下对象的方法:this(C 自身的方法)、参数(传给 m 的对象)、创建的对象(m 创建的对象)、组件(C 的直接实例变量)、全局对象(可访问的全局对象,谨慎使用)。示例中 OrderProcessor 通过 this.validate(order)、new Receipt(order)、this.logger.log(...) 进行调用,而不允许 order.customer.address.city,允许 order.getShippingCity()。

压力抵抗协议

针对常见理由给出回应:

  • “更简单”:好写不等于好维护,链式会制造脆弱代码,应添加暴露所需数据的方法。
  • “只有一条链”:两个点号意味着耦合两个对象,二者变化都会破坏调用方,短链也应消除。
  • “数据就在那里”:结构会变化,封装可隔离变化,应向数据所有者询问。
  • “只是只读”:通过链读取同样会耦合到结构,应直接询问所需内容。

危险信号(应停止并重构)

  • 多个点号:a.b.c.d
  • 链式 getter:getA().getB().getC()
  • 可选链:a?.b?.c?.d
  • 为嵌套访问做空值检查
  • 调用代码中包含结构知识
  • 测试中模拟链式调用

重构链式调用

重构前:const zip = user.getProfile().getAddress().getZipCode(); 重构后:在 User 类中添加 getZipCode() 返回 this.profile.getZipCode();在 Profile 类中添加 getZipCode() 返回 this.address.zipCode;调用方使用 const zip = user.getZipCode();

速查对照

  • company.employees[0].address.city → company.getEmployeeCity(id)
  • order.customer.paymentMethod.last4 → order.getPaymentLast4()
  • user.profile.settings.theme → user.getTheme()
  • car.engine.fuel.level → car.getFuelLevel()

常见借口(均不成立)

  • “更简单”:链式写起来简单,维护更难。
  • “只有一条链”:一条链意味着多重耦合。
  • “数据就在那里”:应通过方法正确暴露。
  • “只是只读”:读取链同样造成耦合。
  • “行数更少”:行数不重要,可维护性才重要。
  • “意图很明显”:明显的耦合仍是耦合。

总结

需要嵌套对象的数据时,应在所有者上添加返回该数据的方法,绝不通过多个对象链式穿透。每个点号都是你承担的一项依赖。