Java 仔想在 Go 中写类似 interface -> abstract class -> sub class 的玩意

10 天前
 assiadamo

实现出来是这个样子,接受各位 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
  */
}
2874 次点击
所在节点    Go 编程语言
40 条回复
assiadamo
10 天前
主要是想让基类的通用方法既可以被子类调用也可以被子类重写,同时也要在编译期检测出必须被子类实现的方法,而不是靠运行时的 panic("implement me")
wetalk
10 天前
Java 是世界上最啰嗦的语言
BeijingBaby
10 天前
最多就是 interface, struct 组合,其他就太啰嗦了。
接口是有必要的,继承是有害的。
log4j
10 天前
”使用嵌套而非继承“
好像官方是这么说的
stormtrooperx5
10 天前
作为 Gopher ,这一坨看着都头疼
sthwrong
10 天前
站在 gopher 的角度,毫无意义。把你这段放到 ai 里,吐槽得比我还狠。懒得贴了,免得毁号,总结一句话就是,你在给 struct 硬塞一个万能爹,其实它只缺一把接口形状的刀子。
litchinn
10 天前
https://go.dev/doc/effective_go#embedding
我之前也问过这个问题,唯一类似的就是这个 embedding ,但是在 go 里能用组合还是用组合吧
sunny352787
10 天前
通常我们定义接口的时候都是

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()
}
}
assiadamo
10 天前
@sunny352787 如果一堆 Enity 都有个写法非常固定的方法,区别只有 Enity 的 type 不同,于是只能每个 Entity 都 copy 相同的代码,改下 type 吗
assiadamo
10 天前
@sunny352787 为什么不用
var _ IFIrst = (*Entity)(nil)
var _ ISecond = (*Entity)(nil)
sunny352787
10 天前
@assiadamo #10 你要是这个理解能力,那我很难跟你解释啊...
spritecn
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
}
assiadamo
10 天前
@sunny352787 你们 gopher 的批判方式真是不知所云,虽然好歹 show 了 code ,提了下为什么要在运行时用断言检测接口是否实现而不是编译时,这么回我,给我整这死出,都在装高手不解决实际问题吗
sunny352787
10 天前
@assiadamo #13 我说了,你这个理解能力我解决不了,我能力有限你找别人吧
mightybruce
10 天前
多用 struct 嵌套,少用 interface, 另外 go 不是面向对象的语言,其接口实现都是鸭子类型。
mightybruce
10 天前
其他用法套用函数式编程,强调 go 不是面向对象的语言。
assiadamo
10 天前
@spritecn 你用的注入的方式吗,把具体逻辑写在每个 Pusher 函数里,但这样在基类自带基础实现时怎么用呢
if pusher != nil {
pusher(track)
} else {
//基础实现
}
这样吗
mightybruce
10 天前
那个老兄的代码 Entity 已经实现了 IFIrst 和 ISecond 的接口
darksword21
10 天前
看着头疼,无法批判
assiadamo
10 天前
@mightybruce 鸭子类型好像和我这里的需求没啥关系,我关注的是代码复用和重写,interface 在实现特定模式比如策略模式和注入时很有用,struct 嵌套我想他本意也是为了复用基类的代码,但比抽象类残废,基类声明也不能指向组合他的类的实例
var b Base = &Sub{Base: NewBase()} // 编译报错
,导致要实现像上面说的策略模式还要在上面套一层 interface

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1155041

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX