通过注入类型查找切片元素的模式

I've attempted to lookup objects inside a interface slice using the type of the object. This current solution I have looks as follows:

package main

import (
    "errors"
    "fmt"
)

type Entity struct {
    children []Childable
}

func (e *Entity) ChildByInterface(l interface{}) (Childable, error) {
    for _, c := range e.children {
        if fmt.Sprintf("%T", c) == fmt.Sprintf("%T", l) {
            return c, nil
        }
    }
    return nil, errors.New("child doesn't exist")
}

type Childable interface {
    GetName() string
}

func main() {
    ent := &Entity{
        []Childable{
            &Apple{name: "Appy"},
            &Orange{name: "Orry"},
            // more types can by introduced based on build tags
        },
    }

    appy, err := ent.ChildByInterface(&Apple{})
    if err != nil {
        fmt.Println(err)
    } else {
        appy.(*Apple).IsRed()
        fmt.Printf("%+v", appy)
    }
}

type Apple struct {
    name string
    red  bool
}

func (a *Apple) GetName() string {
    return a.name
}

func (a *Apple) IsRed() {
    a.red = true
}

type Orange struct {
    name   string
    yellow bool
}

func (o *Orange) GetName() string {
    return o.name
}

func (o *Orange) IsYellow() {
    o.yellow = true
}

https://play.golang.org/p/FmkWILBqqA-

More Childable types (Apple, Orange, ...) can be injected using build tags. So in order to keep the lookup type safe and avoid mistakes, I'm passing an interface{} to the lookup function. The Childable interface also assures the newly injected types implement the correct functions.

This is where things start to get messy. Currently I'm doing a string comparison on both the interface's type and the Childable object's type to see if they match: fmt.Sprintf("%T", c) == fmt.Sprintf("%T", l)

Then I can still only return the Childable interface. So I have to use type assertion to get the correct type: appy.(*Apple)

The boiler plating is to get the child in its correct type has become very tedious and the string comparison to find a match is having a significant performance impact. What better solution can I use to match two interfaces with each other to avoid the performance knock?

As far fmt.Sprintf("%T", c) used reflect under the hood there are no advantages to imply it - better use reflect directly. You can use reference argument as a placeholder of a result instead of return value.

func (e *Entity) ChildByInterface(l Childable) error {
    for _, c := range e.children {
        if reflect.TypeOf(l) == reflect.TypeOf(c) {
            fmt.Println(c)
            reflect.ValueOf(l).Elem().Set(reflect.ValueOf(c).Elem())
            return nil
        }
    }
    return errors.New("child doesn't exist")
}

Now pass a placeholder

apple := &Apple{}
err := ent.ChildByInterface(apple)
//and use it
apple.IsRed()

Working code