对迪米特法则原始定义中构造函数调用规则的困惑
迪米特法则中“新创建对象”规则的困惑解析
先明确原始论文规则的核心边界
对于所有类C,以及C的所有方法M,M发送消息的所有对象必须是:
- M的参数对象,包括self对象;或
- C的实例变量对象。
(M创建的对象、M调用的函数/方法创建的对象,以及全局变量对象均被视为M的参数。)
你困惑的点在于“M调用的方法创建的对象算参数”这条,本质是迪米特法则对临时生成的无依赖对象的特殊放行——这类对象不属于原有系统的持久化状态链,不会让你的代码依赖其他对象的内部结构。
示例代码的合规性判断
看你给出的代码:
function text(BookClass book) { book.pages().last().text() }
- 如果
pages()返回的是新创建的Pages集合实例,last()返回的是新创建的Page实例:完全符合迪米特法则。因为你只和book这个直接参数交互,后续调用的是临时生成的新对象的方法,不需要知道book内部是否持有pages集合,也不依赖book的内部状态。 - 如果
pages()返回的是book内部已持有的Pages实例,last()返回的是该集合中已存在的Page对象:违反迪米特法则。因为此时你的代码依赖了book的内部结构(它有pages属性,pages是可遍历的集合),一旦book的内部实现变更(比如改用chapters替代pages),你的代码会直接失效。
你遗漏的关键:迪米特法则的核心是“信息隐藏”,而非“禁止链式调用”
很多人误解迪米特是禁止链式调用,但它真正禁止的是通过链式调用暴露对象的内部状态。返回新对象的链式调用之所以合规,是因为:
- 你不需要知道新对象的创建逻辑,只需要知道当前方法能返回你需要的对象类型;
- 新对象不依赖原有系统的持久化状态,不会让你的代码和其他对象的内部结构绑定;
- 松耦合的优势体现在:
book的内部实现可以随意修改,只要pages()还能返回符合预期的新对象,你的代码就不需要任何改动。
而访问器的链式调用(返回已有对象)之所以被禁止,是因为它强制你的代码依赖对象的内部结构,打破了信息隐藏的边界——你必须知道book里有pages,pages里有last页,才能写出这段代码,这就是紧耦合的根源。
内容的提问来源于stack exchange,提问作者Povilas Gintutis
相关产品推荐
相关产品推荐

