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

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

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

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

https://ex.noerr.eu.org/t/1155041

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

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

© 2021 V2EX