Фактический тип потерян по вызову - PullRequest
0 голосов
/ 31 декабря 2018

Еще один вопрос о полиморфизме в Go, ссылки: Встраивание вместо наследования в Go , https://medium.com/@adrianwit/abstract-class-reinvented-with-go-4a7326525034

Мотивация: есть интерфейс (с некоторыми методами для работы с «внешним»world ") и несколько структур реализации этого интерфейса.

Существует" стандартная "реализация некоторых из этих методов, в которой общая логика должна быть размещена в одном месте с делегированием (новым) методам вStructs-реализовать-the-interface («подклассы» - это не слово).

Я прочитал ссылку выше и написал некоторый тестовый код.Увы, это не работает так, как я ожидаю, фактический тип структуры теряется, когда вызов интерфейса является косвенным.

В C ++ это называется «нарезкой на основе классов» и происходит при передаче полиморфногокласс по значению.В моем тестовом коде Go я стараюсь передавать по ссылке, и тогда Go не является C ++ (или Java).

Код: https://play.golang.org/p/lxAmw8v_kiW

Встроенный:

package main

import (
    "log"
    "reflect"
    "strings"
)

// Command - interface

type Command interface {
    Execute()

    getCommandString() string
    onData(data string)
}

// Command - implementation

type Command_Impl struct {
    commandString string
    conn Connection
}

func newCommand_Impl(conn Connection, data string, args ...string) Command_Impl {
    var buf strings.Builder
    buf.WriteString(data)
    for _, key := range args {
        buf.WriteString(" ")
        buf.WriteString(key)
    }

    return Command_Impl {
        conn: conn,
        commandString: buf.String(),
    }
}

func (self *Command_Impl) Execute() {
    log.Printf("Command Impl Execute: %s", reflect.TypeOf(self))
    self.conn.execute(self)
}

func (self *Command_Impl) getCommandString() string {
    return self.commandString
}

func (self *Command_Impl) onData(data string) {
    log.Printf("Command Impl onData: %s", data)
}

// Command - subclass

type Command_Login struct {
    Command_Impl

    onDataCalled bool
}

func newCommand_Login(conn Connection) *Command_Login {
    return &Command_Login{
        Command_Impl: newCommand_Impl(conn, "LOGIN", "user@foo.com", "pa$$w0rd"),
    }
}

func (self *Command_Login) onData(data string) {
    log.Printf("Command Login onData: %s", data)

    self.onDataCalled = true
}

// Connection - interface

type Connection interface {
    execute(command Command)
}

// Connection - impelementation

type Connection_Impl struct {
}

func newConnection_Impl() *Connection_Impl {
    return &Connection_Impl{}
}

func (self *Connection_Impl) execute(command Command) {
    log.Printf("Connection execute: %s, %s", command.getCommandString(), reflect.TypeOf(command))
    command.onData("some data")
}

func main() {
    conn := newConnection_Impl()
    command := newCommand_Login(conn)

    // I expect command.Execute to preserve actual type of command all the way through
    // command.conn.execute(self) and then the callback onData from connection to command
    // to use the onData in Command_Login
    //
    // This does not happen however, the test fails
    command.Execute()

    // This does preserve actual type of command, but isn't how I'd like to connect
    // commands and connections...
    //
    //conn.execute(command)

    if command.onDataCalled {
        log.Printf("*** GOOD: Command_Login onData ***was*** called")
    } else {
        log.Printf("*** ERROR: Command_Login onData ***not*** called")
    }
}

Существует интерфейс Command, который определяет некоторые методы.

Существует структура Command_Impl, в которой я хотел бы реализовать некоторый общий код, который далее делегировал бы более детализированным методам в большем количестве структур, которые реализуют тот же самыйинтерфейс («подкласс не слово»), аналогично:

https://stackoverflow.com/a/1727737/2342806

Вопрос:

Вызов command.Execute(), который в свою очередь вызывает conn.execute(self), заканчиваетсявверх "нарезать" объект Command_Login и внутри Connection.execute он превращается в Command_Impl.В результате, onData интерфейсный метод, определенный для Command_Login, не вызывается.

Если я вызываю conn.execute(command), тогда вызывается нужное onData, но я бы не хотелдля соединения моих объектов (например, Command уже имеет Connection, но в основном то, что я написал выше о повторном использовании реализации).

В терминах Go я пытаюсь найти способ делегировать реализация путем встраивания и возможность для делегата перезвонить во включающий тип (который точно настраивает логику делегата).

Увы, кажется,не поддерживаться Go (по крайней мере, я не могу найти способ) - как только вы делегируете во встроенную структуру, ваши вызовы остаются полностью во встроенной структуре, она «не знает», что это часть более крупного объекта, которыйможет потребоваться переопределить некоторые методы встроенной структуры.

1 Ответ

0 голосов
/ 31 декабря 2018

Как насчет делегирования реализации путем реализации интерфейса Execute на минимальной необходимой глубине?

func (self *Command_Login) Execute() {
    self.Command_Impl.Execute()
    log.Printf("Command Login Execute: %s", reflect.TypeOf(self))
    self.onDataCalled = true
}

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

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