为了账号安全,请及时绑定邮箱和手机立即绑定

在 Golang 中实现模板方法模式的优雅方式

在 Golang 中实现模板方法模式的优雅方式

Go
守着星空守着你 2022-01-17 10:52:44
在 Go 中是否有一种优雅的规范方法来实现模板方法模式?在 C++ 中,这看起来像这样:#include <iostream>#include <memory>class Runner {public:    void Start() {        // some prepare stuff...        Run();    }private:    virtual void Run() = 0;};class Logger : public Runner {private:    virtual void Run() override {        std::cout << "Running..." << std::endl;    }};int main() {    std::unique_ptr<Runner> l = std::make_unique<Logger>();    l->Start();    return 0;}在 golang 我写了这样的东西:package mainimport (    "fmt"    "time")type Runner struct {    doRun func()    needStop bool}func (r *Runner) Start() {    go r.doRun()}func NewRunner(f func()) *Runner {    return &Runner{f, false}}type Logger struct {    *Runner    i int}func NewLogger() *Logger {    l := &Logger{}    l.doRun = l.doRunImpl    return l}func (l *Logger) doRunImpl() {    time.Sleep(1 * time.Second)    fmt.Println("Running")}func main() {    l := NewLogger()    l.Start()    fmt.Println("Hello, playground")}但是此代码因运行时空指针错误而失败。基本思想是将派生类(go structs)中的一些功能混合到基类例程中,以使基类状态可以从这个混合派生例程中获得。
查看完整描述

3 回答

?
慕少森

TA贡献2019条经验 获得超9个赞

模板方法模式的本质是它允许您将一个或多个特定函数的实现注入到算法的骨架中。


你可以在 Go 中通过将函数或接口注入到你的Runner. 要实现基本的模板方法模式,您根本不需要您的Logger结构:


package main


import (

    "fmt"

)


type Runner struct {

    run func()

}


func (r *Runner) Start() {

    // some prepare stuff...

    r.run()

}


func runLog() {

    fmt.Println("Running")

}


func NewLogger() *Runner {

    return &Runner{runLog}

}


func main() {

    l := NewLogger()

    l.Start()

}


查看完整回答
反对 回复 2022-01-17
?
猛跑小猪

TA贡献1858条经验 获得超8个赞

Logger嵌入一个指针,当您分配结构时该指针将为 nil。这是因为嵌入不会将所有内容都放在结构中,它实际上创建了一个字段(在您的情况下以Runner类型命名*Runner),并且该语言为您提供了一些语法糖来访问其中的内容。在您的情况下,这意味着您可以通过Runner两种方式访问字段:


l := Logger{}

l.needStop = false

//or

l.Runner.needStop = false

要修复错误,您需要在Runner内部分配字段,Logger如下所示:


l := Logger{Runner:&Runner{}}

或者按值而不是指针嵌入。


查看完整回答
反对 回复 2022-01-17
?
幕布斯7119047

TA贡献1794条经验 获得超8个赞

让模板方法设计模式在 Golang 中发挥作用的关键是正确使用嵌入特性和函数赋值。


下面是一个按预期工作的代码片段。


package main


import (

    "fmt"

)


type Runner struct {

    run func()  // 1. this has to get assigned the actual implementation

}


func NewRunner(i func()) *Runner {

    return &Runner{i}

}


func (r *Runner) Start() {

    r.run()

}


type Logger struct {

    Runner

}


func NewLogger() *Logger {

    l := Logger{}

    l.run = l.loggerRun  // 2. the actual version is assigned

    return &l

}


func (l *Logger) loggerRun() {

    fmt.Println("Logger is running...")

}


func main() {

    l := NewLogger()  // 3. constructor should be used, to get the assignment working

    l.Start()

}

Runner 类型func()根据特定的子类型定义了一个应该接收实际实现的属性。Start()包装对 的调用run(),并且一旦在正确的接收器(基础接收器)上调用,它就能够运行正确的版本run():如果在构造函数中发生这种情况(即NewLogger())方法的实际版本run()被分配给run嵌入的属性类型。


而且,输出是:


Logger is running...


Program exited.

在这里,可以运行代码并对其进行修改以测试此设计模式的任何其他变体。


查看完整回答
反对 回复 2022-01-17
  • 3 回答
  • 0 关注
  • 173 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信