Java 仔想在 Go 中写类似 interface - abstract class - sub class 的玩意
实现出来是这个样子,接受各位 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 ,就会简单很多
RIP mjj 都快把人家薅秃噜皮了,影响都是正常学生。 我的也掉了用了不到半年 以前有学生邮箱就行,直接通过。现在很麻烦了,又得定位在学校附近,又得是教育网,还得传学…
背景:一个代码仓库存在两个版本同时开发的场景,比如当前基于 develop 分支,拉了两个分支 dev_1.0 和 dev_1.1 。现在 dev_1.0 的功能开发完成了,测…
搜到的都是 1 月的消息,有人说没怎么限速,了解下当前的信息 文件分发直接 oss 啊… 腾讯云 200M 南京节点用了 3 个月, 没感觉到限速, 不过总共上行+下行…