实现出来是这个样子,接受各位 Gopher 的批判
package main

import "fmt"

// Require 必须要子类实现的方法
// 对应 abstract method
type Require interface {
Require()
}

// Hook 可能会被子类重写的方法
type Hook interface {
Require
Test1()
Test2()
}

// IBase 类似 Java 的 interface -> abstract class -> sub class 套娃
type IBase interface {
Hook
Common()
}

// Base abstract class
type Base[T Hook] struct {
hook T
}

func NewBaseT Hook *Base[T] {
res := &Base[T]{
hook: hook,
}
return res
}

func (b *Base[T]) Common() {
b.hook.Require()
b.hook.Test2()
b.hook.Test1()
}

func (*Base[T]) Test1() {
fmt.Println("Base.Test1")
}

func (*Base[T]) Test2() {
fmt.Println("Base.Test2")
}

// Sub 抽象类的子类
type Sub struct {
Base[Sub]
}

func NewSub() *Sub {
res := &Sub{}
// 注意 %v 输出可能会有套娃死循环
res.Base = NewBase*Sub
return res
}

// Test1 复用 Base 的 Test1
//func (*Sub) Test1() {
// fmt.Println("Sub.Test1")
//}

// Test2 重写 Base 的 Test2
func (*Sub) Test2() {
fmt.Println("Sub.Test2")
}

// Require 必须要子类实现的 Require 不写会编译报错
func (*Sub) Require() {
fmt.Println("Sub.Require")
}

func main() {
m := make(map[int]IBase)
m[1] = NewSub()
b := m[1]
b.Common()
/*
Output:
Sub.Require
Sub.Test2
Base.Test1
*/
}

主要是想让基类的通用方法既可以被子类调用也可以被子类重写,同时也要在编译期检测出必须被子类实现的方法,而不是靠运行时的 panic("implement me")

Java 是世界上最啰嗦的语言

最多就是 interface, struct 组合,其他就太啰嗦了。
接口是有必要的,继承是有害的。

”使用嵌套而非继承“
好像官方是这么说的

作为 Gopher ,这一坨看着都头疼

站在 gopher 的角度,毫无意义。把你这段放到 ai 里,吐槽得比我还狠。懒得贴了,免得毁号,总结一句话就是,你在给 struct 硬塞一个万能爹,其实它只缺一把接口形状的刀子。

go.dev/doc/effective_go#embedding
我之前也问过这个问题,唯一类似的就是这个 embedding ,但是在 go 里能用组合还是用组合吧

通常我们定义接口的时候都是

type IFIrst interface{
FirstFunc()
}

type ISecond interface{
SecondFunc()
}

type Entity struct{
}

func (e *Entity)FirstFunc(){
print("first called")
}

func (e *Entity)SecondFunc(){
print("second called")
}

func main(){
var obj any
obj = &Entity{}

if first,ok:=obj.(IFirst);ok{
first.FirstFunc()
}

if second,ok:=obj.(ISecond);ok{
second.SecondFunc()
}
}

如果一堆 Enity 都有个写法非常固定的方法,区别只有 Enity 的 type 不同,于是只能每个 Entity 都 copy 相同的代码,改下 type 吗

为什么不用
var _ IFIrst = (*Entity)(nil)
var _ ISecond = (*Entity)(nil)

#10 你要是这个理解能力,那我很难跟你解释啊...

我是从 java 过来的,写了几天我发现这么搞太麻烦了,简单方法需要多个实现的场景,都是直接 type xxxxer func(),like:

type Pusher func(track Track) error

func PushToLog(track Track) error {
slog.Infof("track: %+v", track)
return nil
}

func NewMqttPusherAdapter(mqttPusher *MqttPusher) Pusher {
return mqttPusher.Push
}

你们 gopher 的批判方式真是不知所云,虽然好歹 show 了 code ,提了下为什么要在运行时用断言检测接口是否实现而不是编译时,这么回我,给我整这死出,都在装高手不解决实际问题吗

#13 我说了,你这个理解能力我解决不了,我能力有限你找别人吧

多用 struct 嵌套,少用 interface, 另外 go 不是面向对象的语言,其接口实现都是鸭子类型。

其他用法套用函数式编程,强调 go 不是面向对象的语言。

你用的注入的方式吗,把具体逻辑写在每个 Pusher 函数里,但这样在基类自带基础实现时怎么用呢
if pusher != nil {
pusher(track)
} else {
//基础实现
}
这样吗

那个老兄的代码 Entity 已经实现了 IFIrst 和 ISecond 的接口

看着头疼,无法批判

鸭子类型好像和我这里的需求没啥关系,我关注的是代码复用和重写,interface 在实现特定模式比如策略模式和注入时很有用,struct 嵌套我想他本意也是为了复用基类的代码,但比抽象类残废,基类声明也不能指向组合他的类的实例
var b Base = &Sub{Base: NewBase()} // 编译报错
,导致要实现像上面说的策略模式还要在上面套一层 interface

他在 main 里面断言,我就问了下为什么不在编译时检测

我不想说了,我已经强调了 go 不是面向对象语言, 实现策略模式请用函数式和接口混用的方式。

go 不存在 抽象类,go 也不存在对象( class) 这种, 所以不要再谈什么面向对象。

了解,我这也是写了很多年 Java 思维转不过来,但又需要解决实际问题,只能按这样的写法起模板方便自己。

给一段策略模式代码

package main

import "fmt"

// 定义 RouteStrategy 为一个接口,包含 CalculateTime 方法
type RouteStrategy interface {
 CalculateTime(origin, destination string) int
}

// 使用函数类型作为策略
type StrategyFunc func(origin, destination string) int

// 实现 RouteStrategy 接口的 CalculateTime 方法
func (sf StrategyFunc) CalculateTime(origin, destination string) int {
 return sf(origin, destination)
}

// 实现三种策略:步行、骑行、驾车

func WalkStrategyFunc(origin, destination string) int {
 // 假设固定耗时 30 分钟
 return 30
}

func BikeStrategyFunc(origin, destination string) int {
 // 假设固定耗时 15 分钟
 return 15
}

func DriveStrategyFunc(origin, destination string) int {
 // 假设固定耗时 10 分钟
 return 10
}

// 路线规划器
type RoutePlanner struct {
 strategy RouteStrategy
}

// 设置策略
func (rp *RoutePlanner) SetStrategy(strategy RouteStrategy) {
 rp.strategy = strategy
}

// 估算出行时间
func (rp *RoutePlanner) EstimateTime(origin, destination string) int {
 return rp.strategy.CalculateTime(origin, destination)
}

func main() {
 planner := &RoutePlanner{}

 // 使用步行策略
 walkStrategy := StrategyFunc(WalkStrategyFunc)
 planner.SetStrategy(walkStrategy)
 fmt.Println("Walk Time:", planner.EstimateTime("Home", "School"))

 // 使用骑行策略
 bikeStrategy := StrategyFunc(BikeStrategyFunc)
 planner.SetStrategy(bikeStrategy)
 fmt.Println("Bike Time:", planner.EstimateTime("Home", "School"))

 // 使用驾车策略
 driveStrategy := StrategyFunc(DriveStrategyFunc)
 planner.SetStrategy(driveStrategy)
 fmt.Println("Drive Time:", planner.EstimateTime("Home", "Work"))
}

我没理解错的话,说白了你就是想实现一个 “模板方法模式” refactoringguru.cn/design-patterns/template-method/go/example

简单版的代码就是:

type Worker interface {
 MustImplementStep() // 必须被实现的方法
 OptionalHook() // 一个有默认行为的、可选的钩子方法
}

type BaseWorker struct{}

func (b *BaseWorker) OptionalHook() {
 fmt.Println("-> BaseWorker: 执行默认的钩子逻辑。")
}

type ConcreteWorker struct {
 BaseWorker // 嵌入“基类”,OptionalHook 的默认实现。
}

// MustImplementStep 实现接口中必须被实现的方法
func (c *ConcreteWorker) MustImplementStep() {
 fmt.Println("-> ConcreteWorker: 执行必须实现的步骤。")
}

// 编译期安全检查,如果 ConcreteWorker 未实现 MustImplementStep (注释掉上面方法)会报错
var _ Worker = (*ConcreteWorker)(nil)

// OptionalHook “重写”嵌入的钩子方法。
func (c *ConcreteWorker) OptionalHook() {
 fmt.Println("-> ConcreteWorker: 开始执行重写的钩子逻辑。")

 // super.method()
 c.BaseWorker.OptionalHook()

 fmt.Println("-> ConcreteWorker: 结束执行重写的钩子逻辑。")
}

func RunTemplate(w Worker) {
 fmt.Println("--- 模板开始 ---")
 w.MustImplementStep()
 w.OptionalHook()
 fmt.Println("--- 模板结束 ---")
}

func main() {
 worker := &ConcreteWorker{}
 RunTemplate(worker)
}

Base 里一部分是默认的方法实现,一部分是组合调用的逻辑,你需要把这两部分拆开。

了解了,不把 Common 放在 Base 里面而是改成函数像 RunTemplate 注入 Worker ,就会简单很多