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
的部分,以抢先回答“Implement
Cook()
在你的
食物
界面上。我不明白。然后用
CookAt
这样的方法定义一个车辆界面。这有什么问题吗?编辑,我希望能让它更清楚。我认为我的问题没有得到回答。这正是
cook()一出现故障的解决方案
需要另一个这样的参数。似乎我说得不够清楚,我将编辑我的问题。你可以在界面中添加更多的方法。我认为没有任何方法可以在不更改代码的情况下更改核心设计。@AndreKR:那就不要让
Cook()
获取附加参数。在调用
Cook()之前,将这些附加参数传递给其他函数,即
AddToppings()
。你将如何以这种方式编写
烹饪
函数?与现在编写的一样-接受一个
Food
类型的参数。请检查链接我在你的代码中仔细检查了链接,虽然你接受了一个Food接口,但你不使用它。我相信将接口和行为分开不是一个好主意。你很聪明te right-我仅在函数定义
func-cook(食物、位置)中使用它
。因此,我进行了类型检查,并强制编译器只允许
食品
兼容的变量。尝试传递到此函数,例如
汽车
。确实传递了。但这会使
库克
函数无效。如果未在
库克
上定义指定行为(方法)为什么要将其传递到该函数中?在我看来,嵌入到struct中没有什么帮助:它有助于编译,但忽略了界面设计的要点。如果您确实需要像op那样运行
cook
,要么开始编写像op这样丑陋的类型断言,要么在具体的结构上定义方法。这似乎是一种错误他没有帮助。