Go 变量,该变量可以存储多个类型,但没有共享功能
我有多种类型,我想传递这些类型的数据,比如将它们存储在变量中并传递给函数:Go 变量,该变量可以存储多个类型,但没有共享功能,go,Go,我有多种类型,我想传递这些类型的数据,比如将它们存储在变量中并传递给函数: type Pizza struct { Toppings []string Diameter int } type Steak struct { Weight float64 Doneness string } type Car struct { Speed int } type Chair struct { } func main() { var favoriteF
type Pizza struct {
Toppings []string
Diameter int
}
type Steak struct {
Weight float64
Doneness string
}
type Car struct {
Speed int
}
type Chair struct {
}
func main() {
var favoriteFood interface{}
favoriteFood = Pizza{
Diameter: 20,
}
cook(favoriteFood, Chair{})
}
func cook(food interface{}, vehicle interface{}) {
fmt.Print("Cooking ")
if pizza, ok := food.(Pizza); ok {
fmt.Println("a " + strconv.Itoa(pizza.Diameter) + " cm pizza")
}
if steak, ok := food.(Steak); ok {
fmt.Println("a " + steak.Doneness + " steak")
}
if car, ok := vehicle.(Car); ok {
fmt.Print(" in a car at " + strconv.Itoa(car.Speed) + " km/h")
}
if _, ok := vehicle.(Chair); ok {
fmt.Print(" on a chair")
}
}
我想cook()
接受Pizza
和牛排
作为食物
,但不接受汽车
由于接口是由它们的方法定义的,并且我的类型不共享任何公共方法,所以我不能让它们“实现”接口
我还可以介绍一个识别接收器功能,如下所示:
type Food interface {
IsFood() bool
}
func (f *Pizza) IsFood() bool { return true }
func (f *Steak) IsFood() bool { return true }
这是常见的/惯用的吗?Go提供了支持您的需求的接口。例如,您可以有一个名为
Food
的界面:
type Food interface {
Cook()
}
现在让Pizza
和Steak
满足此界面:
func (p *Pizza) Cook() {
// use Pizza's fields
// ...
}
您现在可以使用CookFood
方法,该方法可以同时接受Pizza
和Steak
:
func CookFood(f Food) {
// ...
f.Cook()
// ...
}
你可以这样称呼它:
func main() {
var favoriteFood = Pizza{
Diameter: 20,
}
CookFood(favoriteFood)
}
使用
Car
调用CookFood
(不实现Cook
方法)会使编译器抛出错误,提供正确的类型安全性。好的,我使用接口更改了您的代码:
接口部分:
type Food interface {
CookedInfo() string
}
func (p Pizza) CookedInfo() string {
return "a " + strconv.Itoa(p.Diameter) + " cm pizza"
}
func (s Steak) CookedInfo() string {
return "a" + s.Doneness + " steak"
}
type Location interface { //chair is hardly a vehicle
Where() string
}
func (c Chair) Where() string {
return "on a chair"
}
func (c Car) Where() string {
return "in a car at " + strconv.Itoa(c.Speed) + " km/h"
}
接口的全部要点(可能不是全部,但非常接近)是隐藏实现细节。例如,Pizza
的大小、名称或其他属性就是这样的细节,函数cook
并不关心这些细节。它也不在乎牛排是否煮得太熟,也不在乎你是否在煮龙,因为龙有一种方法CookedInfo
。每种食物都有自己的方法来提供烹饪信息,供厨师使用。就是这样。它可以防止您编写测试类型的内部列表(当函数作为lib导出时,这甚至变得不可能),并简化您的逻辑。我稍微修改了前面的示例-
您可以尝试使用嵌入并将接口食物
嵌入到具体结构比萨饼和牛排中,但不包括汽车
type Food interface {
Cook()
}
type Pizza struct {
Food
Diameter int
}
func Cook(food Food) {
...
}
这样,只接受与食品
匹配的结构
但别忘了Go是鸭子式语言,所以如果你突然在椅子上实现了方法Cook
,你可以)
然而,它并没有明确地嵌入食物 你应该问自己为什么要做饭。这是不能吃的。想想你的食物,如果它们真的没有共同点(或者至少没有烹饪的共同点),为什么它们有相同的功能?这看起来很像一个设计问题。这就是问题所在,我不想煮一辆车。但是,我可能想在特定的交通工具中烹饪特定的食物:烹饪(f食物,v交通工具)
。(我只添加了关于Vehicle
的部分,以抢先回答“ImplementCook()
在你的食物
界面上。我不明白。然后用CookAt
这样的方法定义一个车辆界面。这有什么问题吗?编辑,我希望能让它更清楚。我认为我的问题没有得到回答。这正是cook()一出现故障的解决方案
需要另一个这样的参数。似乎我说得不够清楚,我将编辑我的问题。你可以在界面中添加更多的方法。我认为没有任何方法可以在不更改代码的情况下更改核心设计。@AndreKR:那就不要让Cook()
获取附加参数。在调用Cook()之前,将这些附加参数传递给其他函数,即AddToppings()
。你将如何以这种方式编写烹饪
函数?与现在编写的一样-接受一个Food
类型的参数。请检查链接我在你的代码中仔细检查了链接,虽然你接受了一个Food接口,但你不使用它。我相信将接口和行为分开不是一个好主意。你很聪明te right-我仅在函数定义func-cook(食物、位置)中使用它
。因此,我进行了类型检查,并强制编译器只允许食品
兼容的变量。尝试传递到此函数,例如汽车
。确实传递了。但这会使库克
函数无效。如果未在库克
上定义指定行为(方法)为什么要将其传递到该函数中?在我看来,嵌入到struct中没有什么帮助:它有助于编译,但忽略了界面设计的要点。如果您确实需要像op那样运行cook
,要么开始编写像op这样丑陋的类型断言,要么在具体的结构上定义方法。这似乎是一种错误他没有帮助。