0

Лукавство в шаблонном методе на Go

Маздайщик 4 роки тому оновлений 4 роки тому 1

В Go наследование реализации отделено от полиморфизма. Можно создать «наследника структуры», сделав первое её поле безымянным. Можно у «родительской структуры» и у «дочерней» написать одноимённые методы. Но методы «родительской» структуры не будут полиморфно вызывать методы «дочерней», а будут статически вызывать свои методы, не смотря на то, что «родительская» создана не сама по себе, а как первое поле «дочерней».

Поэтому такой пример шаблонного метода на Go просто так не перепишется:

class Employee {
  private String name;

  Employee(String name) {
    this.name = name;
  }

  final String greeting() {
    return "Hello, my name is " + this.name + ", I am a " + post();
  }

  protected abstract String post();
}

class Manager implements Employee {
  Manager(String name) {
    super(name);
  }

  protected String post() {
    return "manager";
  }
}

class Welder implements Employee {
  private int degree;

  Welder(String name, int degree) {
    super(name);
    this.degree = degree;
  }

  protected String post() {
    return "сварщик " + degree + " разряда";
  }
}

А ведь в этом фишка шаблонного метода — в базовом классе можем пользоваться состоянием базового класса и абстрактными методами, определёнными в потомках. (Кстати, на C++ чисто виртуальный метод post() вообще можно объявить private — виртуальные приватные методы в C++ в потомках переопределять можно, но вызывать нельзя. Но это к делу не относится.)

В примере на сайте базовый класс пустой, не имеет данных, что создаёт обманчивую видимость простоты реализации.

Шаблонный метод существенно использует возможность наследования реализации вместе с полиморфизмом, чего в Go нет. На эту ограниченность нужно обратить внимание в примере.

Пример для шаблонного метода выше на Go выглядит примерно так:

package main

import (
    "fmt"
)

type IEmployee interface {
    post() string
}

type Employee struct {
    name    string
    virtual IEmployee
}

func (this *Employee) greeting() string {
    return "Hello, my name is " + this.name + ", I am a " + this.virtual.post()
}

type Manager struct {
    Employee
}

func (this *Manager) post() string {
    return "Manager"
}

func makeManager(name string) *Manager {
    result := &Manager{Employee{name, nil}}
    result.Employee.virtual = result
    return result
}

type Welder struct {
    Employee
    degree int
}

func (this *Welder) post() string {
    return "сварщик " + fmt.Sprint(this.degree) + " разряда"
}

func makeWelder(name string, degree int) *Welder {
    result := &Welder{Employee{name, nil}, degree}
    result.Employee.virtual = result
    return result
}

func main() {
    m := makeManager("Петя")
    w := makeWelder("Коля", 6)
    fmt.Println(m.greeting())
    fmt.Println(w.greeting())
}

Можно запустить: https://play.golang.org/p/ajvp-0iNpbR