Еще один вопрос о полиморфизме в 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 (по крайней мере, я не могу найти способ) - как только вы делегируете во встроенную структуру, ваши вызовы остаются полностью во встроенной структуре, она «не знает», что это часть более крупного объекта, которыйможет потребоваться переопределить некоторые методы встроенной структуры.