嵌入golang是否违反得墨忒耳法则?

Does embedding in golang violate law of demeter?

这就是 Effective GOEmbedding in golang

的评价

When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one

我有一段代码 Struct User 定义如下

type User struct {
    Name     string
    Password string
    *sql.Tx
}

然后我调用 u.Query("some query here") 等。我特地这样做是为了避免像 u.Transaction.Query("query") 这样的调用,这显然违反了 得墨忒耳定律 .现在,在阅读了文档和有效的 go 之后,我也对第一种方法的优点表示怀疑。 我是否违反了 Demeter 法则?如果是,我该如何避免?

嵌入概念有点违反 Law of Demeter 因为它 没有隐藏 类型被嵌入的事实 if type 本身是 exported。请注意,嵌入未导出的类型 不会 违反 LoD(您不能引用未导出的字段和方法)。

但这不会强制您以同样违反 LoD 的方式引用提升的字段或方法。嵌入本身只是一种技术,因此您可以 "outsource" 将通用的共享代码用于其他类型;或者从另一个角度来看,在创建新类型时使用其他类型。您引用嵌入类型的提升字段或方法的方式可能违反法律。

正如您所说,如果您将其称为 u.Tx.Query(),这显然违反了 得墨忒耳定律:您正在使用 [=12] 的实现细节=] 嵌入 *sql.Tx.

但是如果你这样称呼它:u.Query() 没关系。这种形式没有暴露或利用嵌入 *sql.Tx 的事实。如果实现发生变化并且 *sql.Tx 将不再嵌入(例如,它被更改为 "regular" 字段或完全删除,并添加 User.Query() 方法),此表单将继续工作。

如果您不想允许访问导出的嵌入类型的字段值,请将其设置为未导出的常规字段并添加一个 "explicit" User.Query() 可以委托给该字段的方法, 例如:

type User struct {
    Name     string
    Password string
    tx       *sql.Tx // regular field, not embedded; and not exported
}

func (u *User) Query(query string, args ...interface{}) (*sql.Rows, error) {
    return u.tx.Query(query, args...)
}

补充说明:

在示例中,如果使用 u.Query(),使用它的客户端不会受到影响,如果 User 的内部结构发生变化(u.Query() 是否表示升级无关紧要method 或者表示User的方法,即:User.Query()).

如果 sql.Tx 发生变化,是的,u.Query() 可能不再有效。但是不兼容的 sql.Tx 不太可能发生。如果您是已更改包的开发人员,并且进行了不兼容的更改,则您有责任更改依赖于您的不兼容更改的其他代码。这样做(正确更新 u.Query())调用 u.Query() 的客户端不会受到影响,客户端仍然不需要知道幕后发生的变化。

这正是 LoD 所保证的:如果您使用 u.Query() 而不是 u.Tx.Query(),如果 User 在内部发生变化,则调用 u.Query() 的客户端不需要知道或担心。 LoD 不是坏事。你不应该放弃它。你可以选择你遵循的原则,但你也应该思考,而不是不惜任何代价一直遵循所选原则所要求的一切。

还有一点要明确:LoD 不涉及 API 不兼容的更改。它提供的是,如果遵循,实体的内部更改将不会对使用该实体的 "public" 面的其他实体产生影响。如果 sql.Tx 发生剧烈变化,Tx.Query() 将不再可用,那不是 LoD "covered"。