包term
:
type Num interface {
IsNeg() bool
Add(Num) Num
}
type Term struct {
Coeff Num
Var string
}
外包frac64
type Frac64 struct {
Numer uint64
Denom uint64
Neg bool
}
func (a Frac64) Add(b Frac64) Frac64 { // Pretend this is implemented }
包client
type Frac Frac64
func (f Frac) IsNeg() bool { return f.Neg }
func (f Frac) Add(v Num) Num { // Call Frac64's Add here? }
我将如何为Add
实现Frac
以便它实现Num
接口?
外部包frac64
只是一个例子。我不打算用它。
这里的目标(我应该更清楚)是我的包暴露了使用Term
作为其两个属性之一的struct Num
。现在,我希望我的包的用户能够使用big.Rat
或Frac64
或int64
或rune
或他们想要的任何东西,只要他们实现Num
接口。
我遇到的问题是尝试为一个结构实现Num
接口,该结构已经具有与Num
中的函数同名的函数。这就是Frac64
的用武之地。我也可以用big.Rat
作为例子,因为它还有一个叫做Add
的函数。
我不能改变Frac64
(或big.Rat
)的实现,我也不能用Add
函数扩展它,因为它已经存在。这就是为什么我试图使用type Frac Frac64
(或type Frac big.Rat
)然后尝试扩展Frac
。
我没有为Num
实施Frac
,因为我无法从Frac64
的Add
函数调用Frac
的Add
。
你可以用embedding解决这个问题......
type Frac struct {
*Frac64
}
现在Frac
可以使用Frac64
的方法,不需要重写它们。
// `Frac.New(numer, denom, bool)` would remove this implementation leak.
foo := Frac{
&Frac64 {
Numer: 45,
Denom: 99,
Neg: false,
},
}
fmt.Println(foo.IsNeg())
但是当我们尝试使用Add
时,有一个障碍。
// cannot use foo (type Frac) as type Frac64 in argument to foo.Frac64.Add
fmt.Println(foo.Add(foo))
嵌入仅适用于继承方法。当用作参数时,它不会为您使用嵌入式引用,您必须明确地这样做。
真正的问题是func (a Frac64) Add(b Frac64) Frac64
不满足Num接口。如果我们确定它工作正常,因为Frac
实现Num
。
func (a Frac64) Add(b Num) Num {
return Frac64{
Numer: 12,
Denom: 23,
Neg: false,
}
}
这是有效的,但是从一个更具体的类型Frac
构建一个不太具体的类型Frac64
是很尴尬的。
从这里开始变得更加明显,Frac
是一个带有一些扩展的Num
。 Frac
应该是一个扩展Num
并添加分子和分母的接口。 Frac64
成为实施Frac
的类型。
type Num interface {
IsNeg() bool
Add(Num) Num
}
type Frac interface {
Numer() uint
Denom() uint
Num
}
type Frac64 struct {
numer uint64
denom uint64
neg bool
}
func (f Frac64) IsNeg() bool {
return f.neg
}
func (f Frac64) Numer() uint {
return uint(f.numer)
}
func (f Frac64) Denom() uint {
return uint(f.denom)
}
func (a Frac64) Add(b Num) Num {
// Just a placeholder to show it compiles.
return Frac64{
numer: 12,
denom: 34,
neg: false,
}
}
这对练习来说很好。在生产中,考虑使用big.Rat
。
您实现它以使其具有与接口相同的签名;因此必须将其命名为Add
,它必须采用Num
类型的单个参数,并且必须返回Num
类型的单个值。请注意,这并不意味着它可以接受或返回实现Num
的类型的值 - 签名必须相同:
func (a Frac64) Add(b Num) Num {
// Pretend this is implemented
// It can return anything that implements Num
}