gist https://gist.github.com/anonymous/68c3b072538ec48c2667f7db276e781b是我在现有代码库中遇到的重复golang代码模式的简化示例,我试图将其记录下来。从它的用法来看,它似乎与mixin类似,但是我或我的任何同事在golang之前都没有真正看到过这种模式。谁能告诉我这种模式的适当的既定名称?
要点试图说明:
它似乎并不严格满足mixin的大部分定义,因为它在编写主机对象时以超出M的知识的方式操作(可能会修改主机状态)并且不会直接将方法添加到主机类型:
它似乎并没有像一些自称为golang mixins'我找到了:
答案 0 :(得分:4)
您的示例只是带有公开结构字段的组合。虽然这与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
也可以Whisperer
和RoboticVoice
。
您可以编写类似的代码,Whisper
和Yell
都可以使用Person
和RoboticVoice
s的不同实现来编写。
最后但同样重要的是,您仍然可以通过让结构本身实现这些方法来覆盖行为。
以下是完整的示例代码和Golang游乐场的链接:
Yeller
答案 1 :(得分:2)
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。
所以,这样一来,这是一个混合的例子吗?不,我不会这么说。我回答这个问题时遇到的最大问题并不是你的模式不遵循为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是非常合理的。