golang代码是否有一个已建立的模式名称,它似乎与mixin相似

时间:2016-10-17 16:15:05

标签: go

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

要点试图说明:

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

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

它似乎并没有像一些自称为golang mixins'我找到了:

2 个答案:

答案 0 :(得分:4)

TLDR

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

较长的

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

我会自己给一个人并告诉他为什么这样有用。

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

type Yeller interface {
    Yell(message string)
}

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

谁能喊叫?好吧,人们大叫,所以我们会创造一个。我们当然可以直接在人员上实现Yeller界面:

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使用Person的{​​{1}}。

Yeller

还有另一种可能性。我们可以通过将调用传递给// Will work Yell(person.Yeller, "No") 的{​​{1}}来Person实施Yeller

Person

但是这迫使我们写了很多样板代码,我们mixin的方法计数乘以“实现”的次数。

我们可以使用Go的 mixin 设施做得更好。

Yeller

现在func (p *Person) Yell(message string) { p.Yeller.Yell(message) } // Will work again! Yell(person, "Yes") type Person struct { Yeller } p := &Person { /* Add some Yeller to the mix here */ } Yell(p, "Hooray") ,将Person调用传递给已包装的Yeller。不需要样板。

为什么这有用?想象一下,您还希望Yell,创建一个Yeller界面,并且您想要一个Whisper也可以WhispererRoboticVoice

您可以编写类似的代码,WhisperYell都可以使用PersonRoboticVoice s的不同实现来编写。

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

以下是完整的示例代码和Golang游乐场的链接:

Yeller

Playground

答案 1 :(得分:2)

什么是mixin?

mixin基本上是一段代码('混合在'中)到另一个代码:

// WARNING: Messy, untested C code ahead
// Illustrates mixins in a non-OO language.
// mixin_hasname.h
    char *name;
// mixin_hasname_impl.h
struct hasname_vtable { // Vtable for storing mixin functions. Saves a lot of memory in the long run, as only one vtable has to be defined per type.
    char *(*getname)(void *self);
};

#define CAT(X,Y) X##Y
#define CATX(X,Y) CAT(X,Y)
#define GEN_HASNAME_VTABLE(TYPENAME,PRENAME)\
static struct hasname_vtable {\
    .getname = CATX(PRENAME,getname)\
} CATX(PRENAME,vtable);\
static char *CATX(PRENAME,getname)(void *self) {\
    return ((TYPENAME *)self)->name;\
}
// main.c
struct my_c_struct {
// include our mixin fields
#include <mixin_hasname.h>
    int x, y, z;
};

// include our mixin implementation
#include <mixin_hasname_impl.h>

// generate an implementation for our struct
GEN_HASNAME_VTABLE(struct my_c_struct, mycstruct_)

int indirect(struct my_c_struct *x, struct hasname_vtable *hasname_vt) {
    printf("%s\n", hasname_vt.getname(x));
}

int main(void) {
    struct my_c_struct x;

    x.name = "Name";
    x.x = 0, x.y = 0, x.z = 0;

    printf("%s\n", mycstruct_getname(&x)); // Notice we never had to define mycstruct_getname ourselves; we avoided code duplication
    indirect(&x, mycstruct_vtable); // Generally, vtables are passed. Some languages pass typeid's or function pointers, however. (Few put their vtable in their data type (in this case `x`) because of the inefficient memory usage. In the case of C, though, it saves you from having to pass your vtable everytime you call an indirect function).

    return 0;
}

对于OOP中的mixin也是如此,但是OOP对mixin增加了很多限制。例如,mixins可能无法访问私有变量,子类成员,类方法等.Golang不是真的OO。

这个代码模式可以称为mixin吗?

所以,这样一来,这是一个混合的例子吗?不,我不会这么说。我回答这个问题时遇到的最大问题并不是你的模式不遵循为OO定义的定义(因为Golang不是OO),但它并不是真的&#39;混合在&#39;代码,它只是将其存储在自己的领域。

struct struct_with_name_field {
    char *name;
}
// main.c
struct my_c_struct {
    struct struct_with_name_field namefield;
    int x, y, z;
}

int main(void) {
    struct my_c_struct x;

    x.namefield.name = "Name";
    x.x = 0, x.y = 0, x.z = 0;

    return 0;
}

但是,我确实认为将这种模式称为团队或文档中的混合是完全合理的,特别是因为据我所知,Golang中的真正混合是不可能的,并且这种模式尽可能接近。

摘要

Golang不允许你定义mixins(只有接口),所以你的模式不是mixin。但是,考虑到mixin的定义(特别是在OOP之外),以及你的模式可能是最接近Golang的mixins的事实,无论如何将它称为mixin是非常合理的。