douxiaochun4964 2019-02-10 11:21
浏览 300

在没有switch语句的情况下在运行时进行选择实现

I want to select implementation of an interface at runtime, using provided string. I don't want to use switch statement - the code should be generic and work with any new struct implementing the interface without need to modification (open/closed).

Let's say I have following structure:

type Fooer interface {
    Foo()
}

type A struct{}

func (_ *A) Foo() {
    fmt.Println("Calling A")
}

type B struct{}

func (_ *B) Foo() {
    fmt.Println("Calling B")
}

type C struct{}

func (_ *C) Foo() {
    fmt.Println("Calling C")
}

Then, I'd like to do something like:

func main() {
    // let's pretend it's user input
    input := []string{"C", "A", "C"}

    for _, className := range input {
        // struct := StructFromName(className)
        // struct.Foo()
    }
}

To print:

Calling C
Calling A
Calling C

https://play.golang.org/p/mOW5miz5LdU

To my disappointment, go runtime keeps no registry of struct names and as a result, no StructFromName(className) is available. I wouldn't like to create such registry by my own as well. Disclaimer: I write mainly in java and I have OOP habits, so I expected something as easy as

Class<?> clazz = Class.forName(className);
Object object = clazz.newInstance();
Foo foo = (Foo) object;
foo.Foo();

I tried another approach, with methods instead of interface, and it works but it feels hacky and non-idiomatic:

type Hack struct{}

func (_ *Hack) FooA() {
    fmt.Println("Calling A")
}

func (_ *Hack) FooB() {
    fmt.Println("Calling B")
}

func (_ *Hack) FooC() {
    fmt.Println("Calling C")
}

func main() {
    hack := &Hack{}

    // let's pretend it's user input
    input := []string{"FooC", "FooA", "FooC"}

    for _, funcName := range input {
        reflect.ValueOf(hack).MethodByName(funcName).Call(nil)
    }
}

https://play.golang.org/p/3ueEuVV2Hx9

Is there a better way?

  • 写回答

1条回答 默认 最新

  • dongluobei9359 2019-02-10 12:44
    关注

    The best approach to this type of thing is indeed a registry. I suggest using the registry in the database/sql package of the standard library for guidance.

    Each SQL driver (MySQL, PostgreSQL, etc), registers itself in its own init() function by calling sql.Register.

    This makes for a clean dependency tree (no circular dependencies), and allows for ad-hoc implementations of your type, possibly even from third parties.

    评论

报告相同问题?

悬赏问题

  • ¥15 基于卷积神经网络的声纹识别
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 CSAPPattacklab
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 stm32开发clion时遇到的编译问题