Golang的组合继承如何将Commands调度到合适的CommandHandler?
How to dispatch Commands to the right CommandHandler by the composition inheritence of Golang?
我想学习用 Go 编程,我决定作为一个宠物程序,我会为几个 CommandHandler 做一个简单的不同命令的调度程序(如果 CommandHandler 与它应该处理的命令同名。)
我的问题是,当我想要一个 CommandHandlerManager 来发布将分派到正确的 CommandHandler 的命令时,它告诉我我需要一个 HelloWorldCommand 的具体实现,因为 HelloWorldCommandHandler 没有实现接口命令。
编译时出现错误信息
E:\Desktop\ManBear\golang\src>go run main.go
# command-line-arguments
.\main.go:71: cannot use cmdHandler (type HelloWorldCommandHandler) as type CommandHandler in array or slice literal:
HelloWorldCommandHandler does not implement CommandHandler (wrong type for Handle method)
have Handle(HelloWorldCommand)
want Handle(Command)
我需要有人向我解释我做错了什么,我猜这意味着我应该为我的 HelloWorldCommandHandler 单独实现一个单独的 func 方法,例如以下:
func (ch HelloWorldCommandHandler) Handle(cmd Command) {
fmt.Println("HelloWorldCommandHandler handled the basic command with name --> " + cmd.GetName())
}
但它造成了更多与类型相关的错误。
我将尝试做一个工作示例,并解释我做错的方式和原因。
谢谢。
这是我的代码:
package main
import (
"fmt"
"strconv"
)
type Command interface {
GetName() string
}
type CommandHandler interface {
Command
Handle(cmd Command)
}
type HelloWorldCommand struct {
Command
Name string
Age int
}
func (cmd HelloWorldCommand) GetName() string {
return "HelloWorldCommand"
}
type HelloWorldCommandHandler struct {
CommandHandler
}
func (cmd HelloWorldCommandHandler) GetName() string {
return "HelloWorldCommand"
}
func (ch HelloWorldCommandHandler) Handle(cmd HelloWorldCommand) {
fmt.Println("Hello World! My name is " + cmd.Name + " and I'm " + strconv.Itoa(cmd.Age) + " years old!")
}
type CommandHandlerManager struct {
CommandHandlers []CommandHandler
}
func (chm CommandHandlerManager) Publish(cmd Command) {
for _, cmdHandler := range chm.CommandHandlers {
if cmd.GetName() == cmdHandler.GetName() {
cmdHandler.Handle(cmd)
}
}
}
func main() {
fmt.Println("Hey my friend!")
cmd := HelloWorldCommand {Name: "ManBear", Age: 357}
cmdHandler := HelloWorldCommandHandler {}
fmt.Println(cmd.GetName())
fmt.Println(cmdHandler.GetName())
cmdHandler.Handle(cmd)
cmdHandlerManager := CommandHandlerManager {
CommandHandlers: []CommandHandler {
cmdHandler, // <-- the error is here
},
};
}
更新:
出于好奇,这是我的宠物程序的功能版本
感谢 Dean Elbaz 的帮助,建议使用类型断言,这使得通过正确的 CommandHandler 和使用正确的值集来处理命令成为可能每个命令都有。
package main
import (
"fmt"
"strconv"
"time"
)
type Command interface {
GetName() string
}
type CommandHandler interface {
Command
Handle(cmd Command)
}
const HelloWorldCommandName string = "HelloWorldCommand"
type HelloWorldCommand struct {
Command
Name string
Age int
}
func (cmd HelloWorldCommand) GetName() string {
return HelloWorldCommandName
}
// Basic Hello World
type HelloWorldCommandHandler struct {
CommandHandler
}
func (cmd HelloWorldCommandHandler) GetName() string {
return HelloWorldCommandName
}
func (ch HelloWorldCommandHandler) Handle(cmd Command) {
fmt.Println("Hello World!\n----------------------------------------\n")
}
// Hello World with Name and Age
type HelloWorldWithNameAndAgeCommandHandler struct {
CommandHandler
}
func (cmd HelloWorldWithNameAndAgeCommandHandler) GetName() string {
return HelloWorldCommandName
}
func (ch HelloWorldWithNameAndAgeCommandHandler) Handle(cmd Command) {
var helloWorldCommand HelloWorldCommand = cmd.(HelloWorldCommand)
fmt.Println("Hello World! My name is " + helloWorldCommand.Name + " and I'm " + strconv.Itoa(helloWorldCommand.Age) + " years old!\n----------------------------------------\n")
}
const TodayDateTimeCommandName string = "TodayDateTimeCommand"
// Today's DateTime Command
type TodayDateTimeCommand struct {
Command
TimeZone string
}
func (cmd TodayDateTimeCommand) GetName() string {
return TodayDateTimeCommandName
}
type TodayDateTimeCommandHandler struct {
}
func (ch TodayDateTimeCommandHandler) GetName() string {
return TodayDateTimeCommandName
}
func (ch TodayDateTimeCommandHandler) Handle(cmd Command) {
var todayCommand TodayDateTimeCommand = cmd.(TodayDateTimeCommand)
location, err := time.LoadLocation(todayCommand.TimeZone)
if err != nil {
fmt.Errorf("Could not load the Location of the TimeZone. %f", err.Error())
return
}
current_time := time.Now().In(location)
fmt.Println("Today's date and time is " + current_time.String() + " for the time zone: " + todayCommand.TimeZone)
}
// The CommandHandler Manager
type CommandHandlerManager struct {
CommandHandlers []CommandHandler
}
func (chm CommandHandlerManager) Publish(cmd Command) {
for _, cmdHandler := range chm.CommandHandlers {
if cmd.GetName() == cmdHandler.GetName() {
cmdHandler.Handle(cmd)
}
}
}
func main() {
fmt.Println("Hey my friend!\n\n\n")
cmdHandlerManager := CommandHandlerManager {
CommandHandlers: []CommandHandler {
HelloWorldCommandHandler {},
HelloWorldWithNameAndAgeCommandHandler {},
TodayDateTimeCommandHandler {},
},
};
cmd := HelloWorldCommand {Name: "ManBear", Age: 357}
cmdHandlerManager.Publish(cmd)
fmt.Println("~~~~~~~~ other command published ~~~~~~~~~~~~~~~~~~~~~")
cmd2 := TodayDateTimeCommand{ TimeZone: "America/Montreal" }
cmdHandlerManager.Publish(cmd2)
}
Handle
的签名必须完全 func (ch HelloWorldCommandHandler) Handle(cmd Command) {
才能实现接口。
一个快速的解决方法是在 Handle
函数的开头执行 type assertion 以从 Command
)
中获取 HelloWorldCommand
但我认为这可能是一个可能的设计问题的征兆:如果多态性在 Command
上,也许命令本身应该 handle/run?也许在界面中带有 Run() error
?
我想学习用 Go 编程,我决定作为一个宠物程序,我会为几个 CommandHandler 做一个简单的不同命令的调度程序(如果 CommandHandler 与它应该处理的命令同名。)
我的问题是,当我想要一个 CommandHandlerManager 来发布将分派到正确的 CommandHandler 的命令时,它告诉我我需要一个 HelloWorldCommand 的具体实现,因为 HelloWorldCommandHandler 没有实现接口命令。
编译时出现错误信息
E:\Desktop\ManBear\golang\src>go run main.go
# command-line-arguments
.\main.go:71: cannot use cmdHandler (type HelloWorldCommandHandler) as type CommandHandler in array or slice literal:
HelloWorldCommandHandler does not implement CommandHandler (wrong type for Handle method)
have Handle(HelloWorldCommand)
want Handle(Command)
我需要有人向我解释我做错了什么,我猜这意味着我应该为我的 HelloWorldCommandHandler 单独实现一个单独的 func 方法,例如以下:
func (ch HelloWorldCommandHandler) Handle(cmd Command) {
fmt.Println("HelloWorldCommandHandler handled the basic command with name --> " + cmd.GetName())
}
但它造成了更多与类型相关的错误。
我将尝试做一个工作示例,并解释我做错的方式和原因。
谢谢。
这是我的代码:
package main
import (
"fmt"
"strconv"
)
type Command interface {
GetName() string
}
type CommandHandler interface {
Command
Handle(cmd Command)
}
type HelloWorldCommand struct {
Command
Name string
Age int
}
func (cmd HelloWorldCommand) GetName() string {
return "HelloWorldCommand"
}
type HelloWorldCommandHandler struct {
CommandHandler
}
func (cmd HelloWorldCommandHandler) GetName() string {
return "HelloWorldCommand"
}
func (ch HelloWorldCommandHandler) Handle(cmd HelloWorldCommand) {
fmt.Println("Hello World! My name is " + cmd.Name + " and I'm " + strconv.Itoa(cmd.Age) + " years old!")
}
type CommandHandlerManager struct {
CommandHandlers []CommandHandler
}
func (chm CommandHandlerManager) Publish(cmd Command) {
for _, cmdHandler := range chm.CommandHandlers {
if cmd.GetName() == cmdHandler.GetName() {
cmdHandler.Handle(cmd)
}
}
}
func main() {
fmt.Println("Hey my friend!")
cmd := HelloWorldCommand {Name: "ManBear", Age: 357}
cmdHandler := HelloWorldCommandHandler {}
fmt.Println(cmd.GetName())
fmt.Println(cmdHandler.GetName())
cmdHandler.Handle(cmd)
cmdHandlerManager := CommandHandlerManager {
CommandHandlers: []CommandHandler {
cmdHandler, // <-- the error is here
},
};
}
更新:
出于好奇,这是我的宠物程序的功能版本
感谢 Dean Elbaz 的帮助,建议使用类型断言,这使得通过正确的 CommandHandler 和使用正确的值集来处理命令成为可能每个命令都有。
package main
import (
"fmt"
"strconv"
"time"
)
type Command interface {
GetName() string
}
type CommandHandler interface {
Command
Handle(cmd Command)
}
const HelloWorldCommandName string = "HelloWorldCommand"
type HelloWorldCommand struct {
Command
Name string
Age int
}
func (cmd HelloWorldCommand) GetName() string {
return HelloWorldCommandName
}
// Basic Hello World
type HelloWorldCommandHandler struct {
CommandHandler
}
func (cmd HelloWorldCommandHandler) GetName() string {
return HelloWorldCommandName
}
func (ch HelloWorldCommandHandler) Handle(cmd Command) {
fmt.Println("Hello World!\n----------------------------------------\n")
}
// Hello World with Name and Age
type HelloWorldWithNameAndAgeCommandHandler struct {
CommandHandler
}
func (cmd HelloWorldWithNameAndAgeCommandHandler) GetName() string {
return HelloWorldCommandName
}
func (ch HelloWorldWithNameAndAgeCommandHandler) Handle(cmd Command) {
var helloWorldCommand HelloWorldCommand = cmd.(HelloWorldCommand)
fmt.Println("Hello World! My name is " + helloWorldCommand.Name + " and I'm " + strconv.Itoa(helloWorldCommand.Age) + " years old!\n----------------------------------------\n")
}
const TodayDateTimeCommandName string = "TodayDateTimeCommand"
// Today's DateTime Command
type TodayDateTimeCommand struct {
Command
TimeZone string
}
func (cmd TodayDateTimeCommand) GetName() string {
return TodayDateTimeCommandName
}
type TodayDateTimeCommandHandler struct {
}
func (ch TodayDateTimeCommandHandler) GetName() string {
return TodayDateTimeCommandName
}
func (ch TodayDateTimeCommandHandler) Handle(cmd Command) {
var todayCommand TodayDateTimeCommand = cmd.(TodayDateTimeCommand)
location, err := time.LoadLocation(todayCommand.TimeZone)
if err != nil {
fmt.Errorf("Could not load the Location of the TimeZone. %f", err.Error())
return
}
current_time := time.Now().In(location)
fmt.Println("Today's date and time is " + current_time.String() + " for the time zone: " + todayCommand.TimeZone)
}
// The CommandHandler Manager
type CommandHandlerManager struct {
CommandHandlers []CommandHandler
}
func (chm CommandHandlerManager) Publish(cmd Command) {
for _, cmdHandler := range chm.CommandHandlers {
if cmd.GetName() == cmdHandler.GetName() {
cmdHandler.Handle(cmd)
}
}
}
func main() {
fmt.Println("Hey my friend!\n\n\n")
cmdHandlerManager := CommandHandlerManager {
CommandHandlers: []CommandHandler {
HelloWorldCommandHandler {},
HelloWorldWithNameAndAgeCommandHandler {},
TodayDateTimeCommandHandler {},
},
};
cmd := HelloWorldCommand {Name: "ManBear", Age: 357}
cmdHandlerManager.Publish(cmd)
fmt.Println("~~~~~~~~ other command published ~~~~~~~~~~~~~~~~~~~~~")
cmd2 := TodayDateTimeCommand{ TimeZone: "America/Montreal" }
cmdHandlerManager.Publish(cmd2)
}
Handle
的签名必须完全 func (ch HelloWorldCommandHandler) Handle(cmd Command) {
才能实现接口。
一个快速的解决方法是在 Handle
函数的开头执行 type assertion 以从 Command
)
HelloWorldCommand
但我认为这可能是一个可能的设计问题的征兆:如果多态性在 Command
上,也许命令本身应该 handle/run?也许在界面中带有 Run() error
?