I want to learn to program in Go, I decided as a pet program I would do a simple dispatcher of different Commands to several CommandHandler (if the CommandHandler has the same name as the Command it should handle.)
My problem is when I want to have a CommandHandlerManager that publishes a Commands that would be dispatch to the correct CommandHandler, it tells me that I need to have a concrete implementation of my HelloWorldCommand because HelloWorldCommandHandler doesn't implement the interface of Command.
Error message at compilation
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)
I would need that someone explains the me what I'm doing wrong, I guessed it meant that I should implement an alone func method to my HelloWorldCommandHandler for the interface alone like below:
func (ch HelloWorldCommandHandler) Handle(cmd Command) {
fmt.Println("HelloWorldCommandHandler handled the basic command with name --> " + cmd.GetName())
}
But it created more type-related errors.
A working example of what I'm trying to do with an explanation of how and why I'm doing it wrong would be appreciated.
Thank you.
Here's my code:
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
},
};
}
UPDATE:
For the curious, here is a functionning version of my pet program
Thanks to the help of Dean Elbaz by suggesting to use the type assertion, it makes it possible to handle a Command by it correct CommandHandler and use the correct set of values that comes with each Command.
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!
----------------------------------------
")
}
// 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!
----------------------------------------
")
}
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!
")
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)
}