use*_*186 5 c++ go template-method-pattern
在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;
}
Run Code Online (Sandbox Code Playgroud)
在golang我写了这样的东西:
package main
import (
"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")
}
Run Code Online (Sandbox Code Playgroud)
但是此代码因运行时空指针错误而失败.基本思想是将一些功能从派生类(结构化)混合到基类例程,其方式是从这个混合派生例程中获得基类状态.
Logger
嵌入一个指针,当您分配结构时该指针将为 nil。那是因为嵌入不会将所有内容都放入结构中,它实际上创建了一个字段(在您的情况下以Runner
类型命名*Runner
),并且该语言为您提供了一些语法糖来访问其中的内容。在您的情况下,这意味着您可以通过Runner
两种方式访问字段:
l := Logger{}
l.needStop = false
//or
l.Runner.needStop = false
Run Code Online (Sandbox Code Playgroud)
要修复错误,您需要像这样分配Runner
字段Logger
:
l := Logger{Runner:&Runner{}}
Run Code Online (Sandbox Code Playgroud)
或者通过值而不是指针嵌入。
模板方法模式的本质是,它允许您将一个或多个特定函数的实现注入到算法的框架中。
您可以通过在Go中注入一个函数或接口来在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()
}
Run Code Online (Sandbox Code Playgroud)