dongzhukuai8177 2018-04-19 07:36
浏览 16

反映嵌入结构的类型

I'm currently trying myself on some OOP-esque Go, following a tutorial I found online.

So far, it's quite fascinating (reminds me of trying to force OOP into ANSI-C).

However, there's just one thing bothering me that I can't seem to solve.

How would I be able to reflect the Type name of the embedding struct?

All info I found online says one can't reflect over the embedding struct, as the embedded struct has no direct access to it.

Is this entirely accurate? If so, what would be the correct way to solve the following problem (code below)?

Basically, the program prints out the name of three individual animals, followed by the Type name of the embedded struct in parentheses, followed by the respective animal's "sound".

i e for the dog named "Rover", it will print "Rover (Animal): BARK BARK".

Now, obviously, "Rover (Animal)" isn't particularly informative. Ideally, this should be "Rover (Dog)" (the Type name of the embedding struct, rather than the embedded struct).

Therein lies my problem. How may I be able to reflect the Type of the embedding struct, so "Rover (Animal)" becomes "Rover ("Dog"), "Julius (Animal)" becomes "Julius (Cat)", etc?

package main

import (
    "fmt"
    "reflect"
)

type Animal struct {
    Name string
    mean bool
}

type AnimalSounder interface {
    MakeNoise()
}

type Dog struct {
    Animal
    BarkStrength int
}

type Cat struct {
    Basics       Animal
    MeowStrength int
}

type Lion struct {
    Basics       Animal
    RoarStrength int
}

func (dog *Dog) MakeNoise() {
    dog.PerformNoise(dog.BarkStrength, "BARK")
}

func (cat *Cat) MakeNoise() {
    cat.Basics.PerformNoise(cat.MeowStrength, "MEOW")
}

func (lion *Lion) MakeNoise() {
    lion.Basics.PerformNoise(lion.RoarStrength, "ROAR!!  ")
}

func MakeSomeNoise(animalSounder AnimalSounder) {
    animalSounder.MakeNoise()
}

func main() {
    myDog := &Dog{
        Animal{
            Name: "Rover", // Name
            mean: false,   // mean
        },
        2, // BarkStrength
    }

    myCat := &Cat{
        Basics: Animal{
            Name: "Julius",
            mean: true,
        },
        MeowStrength: 3,
    }

    wildLion := &Lion{
        Basics: Animal{
            Name: "Aslan",
            mean: true,
        },
        RoarStrength: 5,
    }

    MakeSomeNoise(myDog)
    MakeSomeNoise(myCat)
    MakeSomeNoise(wildLion)
}

func (animal *Animal) PerformNoise(strength int, sound string) {
    if animal.mean == true {
        strength = strength * 5
    }

    fmt.Printf("%s (%s): 
", animal.Name, reflect.ValueOf(animal).Type().Elem().Name())

    for voice := 0; voice < strength; voice++ {
        fmt.Printf("%s ", sound)
    }

    fmt.Println("
")
}
  • 写回答

1条回答 默认 最新

  • dongqiya9552 2018-04-19 08:22
    关注

    Alright.

    Answering (or at least attempting to) my own question then, providing what I think is probably the right way to deal with this (at least the most straightforward one).

    For brevity I changed all versions of

    func (cat/dog/lion *Cat/*Dog/*Lion) MakeNoise(){}
    

    to

    func (animal *Cat/*Dog/*Lion) MakeNoise(){}
    

    As far as I can tell, that shouldn't really hurt readability, nor introduce any side-effects.

    All iterations of "MakeNoise()" now simply provide a third parameter that is identical to the embedding struct's Type name as a string.

    "PerformNoise()" accepts that parameter ("animalType") and simply appends it to the output

    i e

    fmt.Printf("%s (%s): 
    ", animal.Name, animalType)
    

    The full, updated code:

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type Animal struct {
        Name string
        Type string
        mean bool
    }
    
    type AnimalSounder interface {
        MakeNoise()
    }
    
    type Dog struct {
        Animal
        BarkStrength int
    }
    
    type Cat struct {
        Basics       Animal
        MeowStrength int
    }
    
    type Lion struct {
        Basics       Animal
        RoarStrength int
    }
    
    func (animal *Dog) MakeNoise() {
        animal.PerformNoise(animal.BarkStrength, "BARK", reflect.ValueOf(animal).Type().Elem().Name())
    }
    
    func (animal *Cat) MakeNoise() {
        animal.Basics.PerformNoise(animal.MeowStrength, "MEOW", reflect.ValueOf(animal).Type().Elem().Name())
    }
    
    func (animal *Lion) MakeNoise() {
        animal.Basics.PerformNoise(animal.RoarStrength, "ROAR!!  ", reflect.ValueOf(animal).Type().Elem().Name())
    }
    
    func MakeSomeNoise(animalSounder AnimalSounder) {
        animalSounder.MakeNoise()
    }
    
    func main() {
        myDog := &Dog{
            Animal{
                Name: "Rover", // Name
                mean: false,   // mean
            },
            2, // BarkStrength
        }
    
        myCat := &Cat{
            Basics: Animal{
                Name: "Julius",
                mean: true,
            },
            MeowStrength: 3,
        }
    
        wildLion := &Lion{
            Basics: Animal{
                Name: "Aslan",
                mean: true,
            },
            RoarStrength: 5,
        }
    
        MakeSomeNoise(myDog)
        MakeSomeNoise(myCat)
        MakeSomeNoise(wildLion)
    }
    
    func (animal *Animal) PerformNoise(strength int, sound string, animalType string) {
        if animal.mean == true {
            strength = strength * 5
        }
    
        fmt.Printf("%s (%s): 
    ", animal.Name, animalType)
    
        for voice := 0; voice < strength; voice++ {
            fmt.Printf("%s ", sound)
        }
    
        fmt.Println("
    ")
    }
    

    PS: Just to re-iterate. I wouldn't want to write all my code in this "fake" OOP sort of fashion.

    It adds a boatload of unnecessary abstraction and the need for new design considerations to the process.

    However, I do think it's a nice way to experiment with the language's basic feature-set.

    评论

报告相同问题?

悬赏问题

  • ¥15 使用ue5插件narrative时如何切换关卡也保存叙事任务记录
  • ¥20 软件测试决策法疑问求解答
  • ¥15 win11 23H2删除推荐的项目,支持注册表等
  • ¥15 matlab 用yalmip搭建模型,cplex求解,线性化处理的方法
  • ¥15 qt6.6.3 基于百度云的语音识别 不会改
  • ¥15 关于#目标检测#的问题:大概就是类似后台自动检测某下架商品的库存,在他监测到该商品上架并且可以购买的瞬间点击立即购买下单
  • ¥15 神经网络怎么把隐含层变量融合到损失函数中?
  • ¥15 lingo18勾选global solver求解使用的算法
  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行
  • ¥20 测距传感器数据手册i2c