go - golang代码是否有一个既定的模式名称,看起来类似于mixin

标签 go

要点 https://gist.github.com/anonymous/68c3b072538ec48c2667f7db276e781b是我在尝试记录的现有代码库中遇到的重复 golang 代码模式的最小简化示例。从它的用法来看,它似乎类似于 mixin,但我自己或我的任何同事之前都没有在 golang 中真正见过这种模式。谁能告诉我这个模式合适的既定名称?

要点试图说明:

  • 多个类型(A 和 B)使用的未修改行为实现代码(M 的函数)。
  • 通过组合包含在主机类型(A 和 B)中。
  • 通过 host.behaviour.method(...) 调用导出和访问的行为方法,例如a.Formatter.FormatText(...).
  • 宿主方法受行为影响。
  • 行为持有修改行为执行的状态(私有(private)或导出)。
  • 通过传递行为字段,需要该行为的函数可以使用添加了特定行为的类型,例如b := NewB().Formatter.
  • 可以将许多不同的行为组合成一个给定的类型(为简洁起见,实际上并未显示,但您可以想象将 M1、M2、M3 等包含在 A 或 B 中)。

它似乎并不严格满足 mixin 的大部分定义,因为它在编写时以超出 M 知识范围的方式操作宿主对象(可能修改宿主状态)并且不直接向宿主添加方法输入:

它似乎与我发现的一些自称为“golang mixins”的模式不一样:

最佳答案

长篇小说

  • 这些不是混入。
  • 是的,Go 有 mixins。

更长

您的示例只是带有公开结构字段的组合。虽然这类似于 mixin,但并不相同。你甚至给出了真正的 mixins 的例子(在 Go 中)。

我会给自己一个,并说明为什么它有用。

假设您想对人大喊大叫。然后你需要一个 Yeller 接口(interface)。此外,在这种情况下,我们需要一个 Yell 函数,它接受一个 Yeller 并使用它来喊叫。这是代码:

type Yeller interface {
    Yell(message string)
}

func Yell(m Yeller, message string) {
    m.Yell(message)
}

谁敢骂?好吧,人们大喊大叫,所以我们会创造一个。我们当然可以直接在人身上实现 Yeller 接口(interface):

type Person struct {}

func (p *Person) Yell(message string) { /* yell */ }

// Let a person yell.
person := &Person{}
Yell(person, "No")

现在 Person 被困在一个实现中,也许我们不希望这样。所以,你已经给出了解决方案:

type Person struct {
    Yeller Yeller
}

person := &Person{ /* Add some yeller to the mix here */ }

但是现在,如果我们想让person大喊大叫,我们不能直接使用我们的函数,因为Person没有实现Yeller

// Won't work
Yell(person, "Loud")

相反,我们必须明确告诉 Yell 使用 PersonYeller

// Will work
Yell(person.Yeller, "No")

还有一种可能。我们可以让 Person 通过将调用传递给 PersonYeller 来实现 Yeller

func (p *Person) Yell(message string) {
    p.Yeller.Yell(message)
}

// Will work again!
Yell(person, "Yes")

但这迫使我们编写大量样板代码,我们混合的方法数乘以“实现”的数量。

我们可以使用 Go 的 mixin 工具做得更好。

type Person struct {
    Yeller
}

p := &Person { /* Add some Yeller to the mix here */ }
Yell(p, "Hooray")

现在 Person 是一个 Yeller,将 Yell 调用传递给包装的 Yeller。无需样板文件。

为什么有用?想象一下,您还想要 Whisper,创建一个 Whisperer 接口(interface),并且您想要一个 RoboticVoice 也可以 Whisper大喊

您可以编写类似的代码,PersonRoboticVoice 都可以采用 YellerWhisperer< 的不同实现来组合s.

最后但同样重要的是,您仍然可以通过让结构自己实现方法来覆盖行为。

这里是完整的示例代码和 Golang Playground 的链接:

package main

import (
    "fmt"
)

func main() {
    Yell(&Person{ Yeller: yeller("%s!!!\n") }, "Nooooo")
    Yell(&RoboticVoice{Yeller: twiceYeller("*** %s ***")}, "Oh no")
    Whisper(&Person{ Whisperer: whisperer("Sssssh! %s!\n")}, "...")
    Whisper(&RoboticVoice{ Whisperer: whisperer("Sssssh! %s!\n")}, "...")
}

type Yeller interface {
    Yell(message string)
}

func Yell(y Yeller, message string) {
    y.Yell(message)
}

type Whisperer interface {
    Whisper(message string)
}

func Whisper(w Whisperer, message string) {
    w.Whisper(message)
}

type Person struct {
    Yeller
    Whisperer
}

type RoboticVoice struct {
    Yeller
    Whisperer
}

func (voice *RoboticVoice) Yell(message string) {
    fmt.Printf("BEEP! ")
    voice.Yeller.Yell(message)
    fmt.Printf(" BOP!\n")
}

func (voice *RoboticVoice) Whisper(message string) {
    fmt.Printf("Error! Cannot whisper! %s\n", message)
}

type yeller string

func (y yeller) Yell(message string) {
    fmt.Printf(string(y), message)
}

type twiceYeller string

func (twice twiceYeller) Yell(message string) {
    fmt.Printf(string(twice+twice), message, message)
}

type whisperer string

func (w whisperer) Whisper(message string) {
    fmt.Printf(string(w), message)
}

Playground

关于go - golang代码是否有一个既定的模式名称,看起来类似于mixin,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40091142/

相关文章:

go - 如何对包含在模板中的结构 slice 内的 slice 进行范围排列?

amazon-web-services - AWS Golang SDK 是否包含对 Cognito 提供商的支持?

json - 在 Linux 上使用 gaction 更新 Google Home/Assistant 包时,Golang 运行时出现 panic ?

go - 收件地址不会出现在golang smtp客户端发送的电子邮件中

for-loop - 您如何遍历函数产生的所有值?

unit-testing - 模拟 go-logr 并验证它记录的消息?

templates - 使用 Golang 模板如何在每个模板中设置变量?

go - 理解上下文的 "lifespan"

go - 如何使用 Fyne 制作扩展和拉伸(stretch)的布局框

go - 如何使用 golang 从自定义 docker 注册表中提取图像?