实现出来是这个样子,接受各位 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 NewBase[T Hook](hook T) *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](res)
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
*/
}
1
assiadamo OP 主要是想让基类的通用方法既可以被子类调用也可以被子类重写,同时也要在编译期检测出必须被子类实现的方法,而不是靠运行时的 panic("implement me")
|
![]() |
2
wetalk 2 天前 ![]() |
![]() |
3
BeijingBaby 2 天前
最多就是 interface, struct 组合,其他就太啰嗦了。
接口是有必要的,继承是有害的。 |
4
log4j 2 天前
”使用嵌套而非继承“
好像官方是这么说的 |
![]() |
5
stormtrooperx5 2 天前
作为 Gopher ,这一坨看着都头疼
|
6
sthwrong 2 天前 ![]() 站在 gopher 的角度,毫无意义。把你这段放到 ai 里,吐槽得比我还狠。懒得贴了,免得毁号,总结一句话就是,你在给 struct 硬塞一个万能爹,其实它只缺一把接口形状的刀子。
|
![]() |
7
litchinn 2 天前 ![]() https://go.dev/doc/effective_go#embedding
我之前也问过这个问题,唯一类似的就是这个 embedding ,但是在 go 里能用组合还是用组合吧 |
8
sunny352787 2 天前
通常我们定义接口的时候都是
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() } } |
9
assiadamo OP @sunny352787 如果一堆 Enity 都有个写法非常固定的方法,区别只有 Enity 的 type 不同,于是只能每个 Entity 都 copy 相同的代码,改下 type 吗
|
10
assiadamo OP |
11
sunny352787 2 天前
@assiadamo #10 你要是这个理解能力,那我很难跟你解释啊...
|
12
spritecn 2 天前
我是从 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 } |
13
assiadamo OP @sunny352787 你们 gopher 的批判方式真是不知所云,虽然好歹 show 了 code ,提了下为什么要在运行时用断言检测接口是否实现而不是编译时,这么回我,给我整这死出,都在装高手不解决实际问题吗
|
14
sunny352787 2 天前
@assiadamo #13 我说了,你这个理解能力我解决不了,我能力有限你找别人吧
|
![]() |
15
mightybruce 2 天前
多用 struct 嵌套,少用 interface, 另外 go 不是面向对象的语言,其接口实现都是鸭子类型。
|
![]() |
16
mightybruce 2 天前
其他用法套用函数式编程,强调 go 不是面向对象的语言。
|
17
assiadamo OP @spritecn 你用的注入的方式吗,把具体逻辑写在每个 Pusher 函数里,但这样在基类自带基础实现时怎么用呢
if pusher != nil { pusher(track) } else { //基础实现 } 这样吗 |
![]() |
18
mightybruce 2 天前
那个老兄的代码 Entity 已经实现了 IFIrst 和 ISecond 的接口
|
![]() |
19
darksword21 PRO 看着头疼,无法批判
|
20
assiadamo OP @mightybruce 鸭子类型好像和我这里的需求没啥关系,我关注的是代码复用和重写,interface 在实现特定模式比如策略模式和注入时很有用,struct 嵌套我想他本意也是为了复用基类的代码,但比抽象类残废,基类声明也不能指向组合他的类的实例
var b Base = &Sub{Base: NewBase()} // 编译报错 ,导致要实现像上面说的策略模式还要在上面套一层 interface |
21
assiadamo OP @mightybruce 他在 main 里面断言,我就问了下为什么不在编译时检测
|
![]() |
22
mightybruce 2 天前
我不想说了,我已经强调了 go 不是面向对象语言, 实现策略模式请用函数式和接口混用的方式。
|
![]() |
23
mightybruce 2 天前
go 不存在 抽象类,go 也不存在对象( class) 这种, 所以不要再谈什么面向对象。
|
24
assiadamo OP @mightybruce 了解,我这也是写了很多年 Java 思维转不过来,但又需要解决实际问题,只能按这样的写法起模板方便自己。
|
![]() |
25
mightybruce 2 天前 ![]() 给一段策略模式代码
``` 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")) } ``` |
![]() |
26
monmon 2 天前 ![]() 我没理解错的话,说白了你就是想实现一个 “模板方法模式” https://refactoringguru.cn/design-patterns/template-method/go/example
简单版的代码就是: ```go 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) } ``` |
27
unused 2 天前
Base 里一部分是默认的方法实现,一部分是组合调用的逻辑,你需要把这两部分拆开。
|
29
yuezk 1 天前
|
30
kdd0063 1 天前
你都学 go 了为什么不学一学它的范式?推荐去看一下《七周七语言》,你需要看一些完全非 OOP 的语言开拓下对语言范式的视野,强迫自己去写一写 C 也是个方法。
|
![]() |
31
windyboy 1 天前
go 的 interface 要简洁很多
|
![]() |
32
EricYuan1 1 天前
让我想到了一个表情包:
无语,跟你说不下去,典型的 Java 思维 |
34
huangmiao233 21 小时 29 分钟前
有没有像 我这样的 go 里只会用 struct 连 interface 都很少用的...日常就是定义结构体. 写 function 好像也没啥..
|
35
assiadamo OP @huangmiao233 我类比就像把 package 级 function 当 static 用
|
36
leeonsoft 13 小时 36 分钟前
用组合来代替继承,忘记继承
|
37
liuguang 12 小时 34 分钟前
典型的 Java 思维, 现代语言已经不搞继承了,组合优于继承。
|
38
bunny189 10 小时 23 分钟前 via iPhone
跟你说不清楚,典型的 Java 思维
https://i.imgur.com/TKfLzat_d.webp |
39
bunny189 10 小时 19 分钟前 via iPhone
|