在Golang中使用接收器返回函数

时间:2018-09-05 22:18:27

标签: go

我是Golang的新手。我正在尝试使用装饰器,该装饰器返回带有接收器的函数。我该怎么办?

type myStruct struct {
    s string
}

func myFunc(s string) {
    fmt.Println(s)
}

// Here I want to return a function with a receiver
func (*myStruct) myDecorator(fn func(string)) (*myStruct)func(string){
    return (ms *myStruct)func(s string) {
        fn(ms+s)
    }
}

func main() {
    ms := myStruct{"Hello"}
    // Some func is function with receiver as myStruct pointer
    someFunc := myDecorator(myFunc)
    // This is expected to print "Hello world"
    ms.someFunc(" world")
}

1 个答案:

答案 0 :(得分:1)

my comment中所述,您不能在函数调用的返回中修改类型的method set,因此在您的应用程序中无法直接扩展myStruct结构的行为书面的“装饰器”功能。

  

我实际上正在使用此装饰器包装fmt.SprintfSprintlnSprint方法以获得Logger.DebuglnLogger.DebugfLogger.Warning,。 ..方法,其中Logger是自定义结构,因为这些函数几乎具有相同的行为。

您还将无法以通用方式传递fmt.Sprintffmt.Sprintlnfmt.Sprint函数,因为它们共享不同的签名:

func Print(a ...interface{}) (n int, err error)
func Printf(format string, a ...interface{}) (n int, err error)
func Println(a ...interface{}) (n int, err error)

所以您需要分别包装这些​​方法。

对于我来说,目前还不是很清楚,你是如何寻求包装这些方法以产生Logger类型的。

如果您在记录器的顶层需要这些方法中的每一个,则将需要在日志记录包或Logger类型的接口中显式声明它们。尽管它们具有明显的相似性,但您不能在运行时动态绑定每个级别的函数。 go是一种静态类型的语言,因此必须预先声明您的类型。

明确定义类型将是最清晰的解决方案,这也符合键go proverb"Clear is better than clever."

如有必要,可以将每个方法声明为实际执行打印逻辑的类型上内部方法的简单包装,从而将重复的次数降至最低。

这是一个快速的示例,我将其汇总在一起以显示某些DebugDebugfDebugln端点的逻辑。您可以看到核心逻辑由Logger类型的一组通用方法处理,并且可以想象如何轻松实现其他日志级别的端点。

如果重复确实是一个问题,则可以编写一些代码生成器来自动生成特定于日志级别的方法,并调用各种打印函数中显式声明的核心功能。请记住,您可以在同一包中的多个源文件中声明某种类型的接收器,从而允许在某些源文件中生成某些方法,而其他方法则在其他位置显式实现。

package main

import (
    "fmt"
    "io"
    "os"
)

type Level string

const (
    Debug Level = "DEBUG"
    Error       = "ERROR"

// etc.
)

type Logger struct {
    writer       io.Writer
    prependLevel bool
}

func (l *Logger) print(level Level, msg string) {
    var s string
    if l.prependLevel {
        s = string(level) + ": "
    }
    s += msg
    fmt.Fprint(l.writer, s)
}

func (l *Logger) printf(level Level, format string, a ...interface{}) {
    l.print(level, fmt.Sprintf(format, a...))
}

func (l *Logger) println(level Level, msg string) {
    l.printf(level, "%s\n", msg)
}

func (l *Logger) Debug(msg string) {
    l.print(Debug, msg)
}

func (l *Logger) Debugf(format string, a ...interface{}) {
    l.printf(Debug, format, a...)
}

func (l *Logger) Debugln(msg string) {
    l.println(Debug, msg)
}

func main() {
    logger := Logger{os.Stderr, true}
    logger.Debugln("A plain message with a new line")
    logger.Debugf(
        ("The most customizable log entry with some parameters: " +
            "%s %v\n"),
        "myStr", false,
    )
    logger.Debug("A bare print statement")
}

Playground link.


第三方软件包

如果您还没有这样做,我建议您考虑使用Go中的许多第三方日志记录软件包,或者至少评估它们的接口以确定其他人如何完成类似的任务。

Logrus是具有级别的Go记录器。您可以在它们的源文件中看到它们如何在每个日志级别显式声明每个函数的接口,同时使用上述方法的更全面版本来减少核心日志记录实现中的重复:Interface Formatter implementations