Структуры и типы интерфейсов - PullRequest
0 голосов
/ 16 апреля 2020

Может ли кто-нибудь объяснить (или если долго объяснять хотя бы указать мне какую-то документацию), почему этот код ведет себя так, как он. Это немного длинно, но я не мог найти хороший способ сократить его, не теряя слишком много информации.

Что меня смущает, так это то, что мне нужен метод «Найти» для каждого типа структуры. быть распознанным как тип это. С Find только для baseTypeImp он печатает, что каждый из них является baseTypeImp, если он у меня есть для baseTypeImp и advancedBaseTypeImp, то эти два идентифицированы правильно, но не последний тип.

PlaygroundLink

Мой код

package main

import (
    "fmt"
    "reflect"
)

type BaseType interface {
    Name() string
    Find(string) BaseType
    Children() []BaseType
    Options()
}

type baseTypeImp struct {
    name     string
    children []BaseType
}

func (b baseTypeImp) Options() {
    fmt.Println("Not implemented")
}

func (b baseTypeImp) Find(name string) BaseType {
    if b.name == name {
        return b
    }

    for _, c := range b.children {
        if m := c.Find(name); m != nil {
            return m
        }
    }
    return nil
}

func (b baseTypeImp) Name() string {
    return b.name
}
func (b baseTypeImp) Children() []BaseType {
    return b.children
}

type AdvancedBaseType interface {
    Value()
}

type advancedBaseTypeImp struct {
    baseTypeImp
}

func (a advancedBaseTypeImp) Options() {
    fmt.Println("Is implemented")
}

func (a advancedBaseTypeImp) Value() {
    fmt.Println("Value called")
}

func (a advancedBaseTypeImp) Find(name string) BaseType {
    if a.name == name {
        return a
    }

    for _, c := range a.children {
        if m := c.Find(name); m != nil {
            return m
        }
    }
    return nil
}

type reallyAdvancedBaseTypeImp advancedBaseTypeImp

func newThingy(name, variant string, children []BaseType) BaseType {
    base := baseTypeImp{name: name, children: children}
    switch variant {
    case "advanced":
        return advancedBaseTypeImp{baseTypeImp: base}
    case "reallyAdvanced":
        return reallyAdvancedBaseTypeImp{baseTypeImp: base}
    }
    return base
}

func whatType(b BaseType) {
    if b == nil {
        return
    }
    fooType := reflect.TypeOf(b)
    fmt.Println(b.Name(), " is type ", fooType.Name())
    b.Options()
}

func main() {
    advanced := make([]BaseType, 0, 5)
    for i := 0; i < 5; i++ {
        advanced = append(advanced, newThingy(fmt.Sprintf("Advanced %d", i), "advanced", nil))
    }
    reallyAdvanced := make([]BaseType, 0, 2)
    for i := 0; i < 2; i++ {
        reallyAdvanced = append(reallyAdvanced, newThingy(fmt.Sprintf("ReallyAdvanced %d", i), "reallyAdvanced", advanced[i:i+3]))
    }
    basic := newThingy("Basic parent", "basic", reallyAdvanced)
    whatType(basic.Find("Basic parent"))
    for i := 0; i < 2; i++ {
        whatType(basic.Find(fmt.Sprintf("Advanced %d", i)))
        whatType(basic.Find(fmt.Sprintf("ReallyAdvanced %d", i)))
    }
}

Теперь вывод:

Basic parent  is type  baseTypeImp
Not implemented
Advanced 0  is type  advancedBaseTypeImp
Is implemented
ReallyAdvanced 0  is type  baseTypeImp
Not implemented
Advanced 1  is type  advancedBaseTypeImp
Is implemented
ReallyAdvanced 1  is type  baseTypeImp
Not implemented

То, что я хотел бы иметь:

Basic parent  is type  baseTypeImp
Not implemented
Advanced 0  is type  advancedBaseTypeImp
Is implemented
ReallyAdvanced 0  is type  reallyAdvancedBaseTypeImp
Is implemented
Advanced 1  is type  advancedBaseTypeImp
Is implemented
ReallyAdvanced 1  is type  reallyAdvancedBaseTypeImp
Is implemented

Есть ли способ заставить это работать без необходимости выполнять поиск на каждом уровне? Это отчасти побеждает цель иметь общие методы в структуре верхнего уровня. И, конечно, если возможно, объясните, почему он ведет себя так, как он делает.

1 Ответ

3 голосов
/ 16 апреля 2020

Когда вы встраиваете структуру в другую, внешняя структура получает методы встроенной.

type inner struct {
}

func (i inner) f() {}

type outer struct {
  inner
}
...

x:=outer{}
x.f()

Вы можете думать об этом как о syntacti c sugar для:

type outer2 struct {
  i inner
}

y.i.f()

Когда вы вставляете inner без имени поля, вы просто отбрасываете i в звонок. Вы все еще можете вызвать x.inner.f() в первом примере.

Когда вы переопределяете тип, он не получает методы, определенные для его базового типа.

type newtype inner

newtype не определили f(). Однако, если inner также встраивает другие структуры (как вы), то они все еще встроены в новый тип, поэтому эти функции определены:

type deepest struct {}
func (deepest) deep() {}

type inner struct{}
func (inner) in() {}

type outer inner

Выше, тип outer не делает есть функция in, но она имеет deep.

Так что когда вы вызываете reallyAdvancedBaseTypeImp.Find(), то, что вы действительно делаете, это reallyAdvancedBaseTypeImp.baseTypeImp.Find(), который работает с этой частью структуры. Вот почему вы получаете типы, которые вы получаете.

Вы пытаетесь эмулировать переопределение метода. Вы не можете этого сделать. Сформулируйте свою проблему по-другому.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...