перейти выберите реализацию во время выполнения без оператора switch - PullRequest
0 голосов
/ 10 февраля 2019

Я хочу выбрать реализацию интерфейса во время выполнения, используя предоставленную строку.Я не хочу использовать оператор switch - код должен быть универсальным и работать с любой новой структурой, реализующей интерфейс, без необходимости модификации (открытой / закрытой).

Допустим, у меня есть следующая структура:

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")
}

Затем я хотел бы сделать что-то вроде:

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

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

Для печати:

Calling C
Calling A
Calling C

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

К моему разочарованиюgo runtime не ведет реестр структурных имен и, как следствие, StructFromName(className) недоступен.Я бы тоже не хотел создавать такой реестр самостоятельно.Отказ от ответственности: я пишу в основном на языке Java, и у меня есть привычки ООП, поэтому я ожидал чего-то столь же простого, как

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

Я попробовал другой подход, с методами вместо интерфейса, и он работает, но он кажется хакерским и неидиоматика:

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

Есть ли лучший способ?

1 Ответ

0 голосов
/ 10 февраля 2019

Лучший подход к этому типу вещей - это действительно реестр.Я рекомендую использовать реестр в пакете database/sql стандартной библиотеки для руководства.

Каждый драйвер SQL (MySQL, PostgreSQL и т. Д.) Регистрируется в своей собственной функции init()вызывая sql.Register.

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

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