2016-06-04 3 views
3

Есть ли элегантный канонический способ реализации шаблона шаблона шаблона в 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 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") 
} 

Но этот код не с выполнения нулевой ошибки указателя. Основная идея состоит в смешении некоторой функциональности от производных классов (go structs) до подпрограммы базового класса таким образом, что состояние базового класса доступно из этой производной подпрограммы в составе.

ответ

3

Logger Вставляет указатель, который будет равен нулю при распределении структуры. Это потому, что вложение не помещает все внутри структуры, оно фактически создает поле (с именем Runner типа *Runner в вашем случае), и этот язык дает вам синтаксический сахар для доступа к тому, что внутри него. В вашем случае это означает, что вы можете получить доступ Runner поля двух способов:

l := Logger{} 
l.needStop = false 
//or 
l.Runner.needStop = false 

Чтобы исправить ошибку, вам нужно выделить Runner поля внутри Logger так:

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

Или вставлять по значению вместо указателя.

+0

Да, теперь это работает. Но все равно выглядит как-то уродливо. Спасибо, в любом случае – user6256186

2

Суть шаблона метода шаблона заключается в том, что он позволяет вводить в реализации конкретной функции или функции в скелет алгоритма.

Вы можете достичь этого в 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() 
} 
+0

В более сложных случаях нам нужен доступ к состоянию экземпляра Logger && Runner.В коде вы можете достичь этого только путем передачи объекта Logger функции runLog() в качестве аргумента. Я лично считаю, что это еще более уродливее, чем мой пример. – user6256186

0

Ключа иметь Template Method Design Pattern работы в 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. 

Here код может быть запущен, и изменен, чтобы проверить любой другой вариант этого шаблона.

Смежные вопросы