如何通过Golang的组合继承将命令分派给正确的CommandHandler?

时间:2016-09-03 20:26:46

标签: go polymorphism cqrs

我想学习在Go中编程,我决定作为一个宠物程序,我会为几个CommandHandler做一个不同命令的简单调度程序(如果CommandHandler与它应该处理的命令同名。)

我的问题是当我想要一个CommandHandlerManager发布一个将被调度到正确的CommandHandler的命令时,它告诉我我需要有一个具体的HelloWorldCommand实现,因为HelloWorldCommandHandler没有实现Command的接口

编译时出现错误消息

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处理命令,并使用每个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!\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)
}

1 个答案:

答案 0 :(得分:1)

Handle的签名必须完全func (ch HelloWorldCommandHandler) Handle(cmd Command) {才能实现该接口。 快速解决方法是在Handle函数的开头执行type assertion以从HelloWorldCommand获取Command

但我认为这可能是一个可能的设计问题的症状:如果多态性在Command上,那么命令应该处理/运行自己?可能在界面中有Run() error吗?