2015-07-19 39 views
7

说我有两个结构:两种不同类型如何在使用接口的golang中实现相同的方法?

type First struct { 
    str string 
} 
type Second struct { 
    str string 
} 

而且我想他们都实现接口A:

type A interface { 
    PrintStr() //print First.str or Second.str 
} 

这似乎是多余的,以对第一和第二结构像这样实现:

func (f First) PrintStr() { 
    fmt.Print(f.str) 
} 

func (s Second) PrintStr() { 
    fmt.Print(s.str) 
} 

有没有办法让所有的实现接口A的结构都有一个实现?像这样的东西,但它似乎并没有工作:

func (a A) PrintStr() { 
    fmt.Print(a.str) 
} 

谢谢!

+2

拥有2个相同类型的结构似乎有点多余。 – TheHippo

+0

是的,但这是一个玩具的例子。第一和第二可以共享一些领域而不是其他领域。重点是我想要一个函数以两种不同类型完全相同的方式运行,而不需要重复代码。 – Ekaterina

回答

10

,你不行,你可以创建一个基本类型,然后将它嵌入到2结构,因此只需要为基本类型的实现:

type WithString struct { 
    str string 
} 

type First struct { 
    WithString 
} 

type Second struct { 
    WithString 
} 

type A interface { 
    PrintStr() //print First.str or Second.str 
} 

func (w WithString) PrintStr() { 
    fmt.Print(w.str) 
} 

用法:

a := First{ 
    WithString: WithString{ 
     str: "foo", 
    }, 
} 

Complete Example on Playground

Embed documentation

+1

这个魔法在哪里定义了一个不可变成员的结构?从你的答案:'type First struct {WithString}'...有没有关于这个的任何文档?定义一个类型结构为另一种类型? – Joey

+1

@Joey这称为嵌入,它是一个众所周知的技术,请参阅https://golang.org/doc/effective_go.html#embedding – SirDarius

1

也许不是最好的方式来解决你的问题,但你可以使用一个包装,以避免“执行”功能的两倍,是这样的:

type First struct { 
    str StringWrapper 
} 
type Second struct { 
    str StringWrapper 
} 


type StringWrapper struct { 
    str string 
} 
func (f StringWrapper) PrintStr() { 
    fmt.Print(f.str) 
} 

func main() { 
    var a First = First{str:StringWrapper{str: "aaa"}}; 
    a.str.PrintStr(); 
} 
2

如果打印逻辑取决于接口,但不能在结构本身,那么最好是移动打印到工作在接口免费功能。

在你的情况下,该PrintStr方法用于打印一个字符串,其是每个结构中的一员。
在这种情况下,这意味着每个结构应该实现返回用于打印的必要串的接口,并且PrintStr变为采取Printable参数的函数。

type First struct { 
    str string 
} 
type Second struct { 
    str string 
} 

type Printable interface { 
    String() string 
} 

func (p First) String() string { 
    return p.str 
} 

func (p Second) String() string { 
    return p.str 
} 

func PrintStr(p Printable) { 
    fmt.Print(p.String()) 
} 

您使用A界面的非惯用因为接口不应该依赖于它的功能实施

取而代之的是,这个解决方案,你仍然可以保持A接口,但简化各执行:

func (f First) PrintStr() { 
    PrintStr(f) 
} 

func (s Second) PrintStr() { 
    PrintStr(s) 
} 

它仍然是多余的,但逻辑在于,从那里调用的函数,限制了需要在修改打印逻辑的情况下进行复制粘贴。

这种模式是在围棋标准库中常见的,因为许多有用的功能是在他们不能扩展,例如io.Reader接口内置。
这是一个只有一个方法一个简单的界面,但它是从许多其他包彻底使用。
如果您看ioutil.ReadAll函数,可能会认为它可能已被作为io.Reader接口的另一种方法实现,但这样可以让读者更简单,专注于单一方法,同时允许任何实现者免费使用ReadAll。

相关问题