我正在学习Golang,作为使用接口的练习,我正在构建一个玩具程序。我有一些问题,尝试使用“应该实现”两个接口的类型 - 在C++和Java中解决这个问题的一种方法是使用继承(还有其他技术,但我认为这是最常见的)。由于我在Golang中缺少这种机制,我不确定如何处理它。以下是代码:
var (
faces = []string{"Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"}
suits = []string{"Hearts", "Diamonds", "Spades", "Clubs"}
)
type Card interface {
GetFace() string
GetSuit() string
}
type card struct {
cardNum int
face string
suit string
}
func NewCard(num int) Card {
newCard := card{
cardNum: num,
face: faces[num%len(faces)],
suit: suits[num/len(faces)],
}
return &newCard
}
func (c *card) GetFace() string {
return c.face
}
func (c *card) GetSuit() string {
return c.suit
}
func (c *card) String() string {
return fmt.Sprintf("%s%s ", c.GetFace(), c.GetSuit())
}
我想要实现的目标:
- 隐藏我的结构体类型,只导出接口,使代码的客户端只使用 "Card" 接口
- 希望有一个结构体的字符串表示,因此实现了接口并使用 "String()" 方法,以便能够在实例化我的结构体时调用 "fmt.Println()"
问题出现在当我尝试通过 "Card" 接口使用新卡并尝试获取字符串表示时。 我无法将接口作为 "String()" 方法的实现参数传递,因为存在与核心语言级别上的接口可寻址性相关的编译器错误(仍在查阅文档)。下面是一个简单的测试示例,揭示了这个问题:
func TestString(t *testing.T) {
card := NewCard(0)
assert.EqualValues(t, "AceHearts ", card.String(), " newly created card's string repr should be 'AceHearts '")
}
编译器告诉我,"card.String 未定义 (类型 card 没有字段或方法 string)"。我可以在 "Card" 接口中添加 "String()" 方法,但我认为这样做不够简洁:我使用相同模型实现了其他实体,我必须在每个实现中都添加冗余代码;而且已经有一个带有该方法的接口。
对于上述问题,有什么好的解决方案吗? 编辑:(回应一些非常好的评论)
- 我不希望有另一个 Card 接口的实现;我不确定为什么要这样做,即更改接口。
- 我想让 Card 接口隐藏实现细节,并使客户端针对接口编程而不是具体类型。
- 我想始终为所有 "card 结构" 实例(包括通过 Card 接口实例化的实例)提供 String() 接口。我不想只为客户端提供 String 接口。在其他一些语言中,可以通过实现两个接口来实现这一点——多重继承。我不是说这是好还是坏,也不想引发关于这个问题的辩论,我只是陈述一个事实!
- 我的意图是找出语言是否有机制可以同时满足这些要求。如果不可能,或者从设计的角度来看,应该以不同的方式解决问题,那么我准备接受教育。
- 类型断言非常冗长和显式,并且会暴露实现细节——它们有自己的用途,但我认为在我所面临的情况下并不合适。
type assert
,应该被视为某种接口升级,就像新的fs.FS
在 stdlib 中所做的那样;另一个是在客户端代码和您的代码之间定义一个完整而清晰的接口,这意味着添加String()
;还有一个是拥有一个函数formatCard(card Card) string
,它接受接口并提供一个工具来对抽象进行操作 - 如果您期望另一种Card
的实现,则应使用此选项。 - leaf bebopString()
添加到Card
的方法集中,或者明确从另一个接口继承Card
。@celavek - mkopriva