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
  */
}
2633 次点击
所在节点    Go 编程语言
39 条回复
assiadamo
3 天前
@mightybruce 他在 main 里面断言,我就问了下为什么不在编译时检测
mightybruce
3 天前
我不想说了,我已经强调了 go 不是面向对象语言, 实现策略模式请用函数式和接口混用的方式。
mightybruce
3 天前
go 不存在 抽象类,go 也不存在对象( class) 这种, 所以不要再谈什么面向对象。
assiadamo
3 天前
@mightybruce 了解,我这也是写了很多年 Java 思维转不过来,但又需要解决实际问题,只能按这样的写法起模板方便自己。
mightybruce
3 天前
给一段策略模式代码
```
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"))
}
```
monmon
3 天前
我没理解错的话,说白了你就是想实现一个 “模板方法模式” 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)
}
```
unused
3 天前
Base 里一部分是默认的方法实现,一部分是组合调用的逻辑,你需要把这两部分拆开。
assiadamo
3 天前
@monmon 了解了,不把 Common 放在 Base 里面而是改成函数像 RunTemplate 注入 Worker ,就会简单很多
yuezk
2 天前
每个语言都有息的习惯 (idiomatic)。入乡随俗,写 Go 就按 Go 的语言习惯来,才能扩宽视野。有点说教
kdd0063
2 天前
你都学 go 了为什么不学一学它的范式?推荐去看一下《七周七语言》,你需要看一些完全非 OOP 的语言开拓下对语言范式的视野,强迫自己去写一写 C 也是个方法。
windyboy
2 天前
go 的 interface 要简洁很多
EricYuan1
1 天前
让我想到了一个表情包:

无语,跟你说不下去,典型的 Java 思维
assiadamo
1 天前
@EricYuan1 可以发一下,P 一个 Go 土拨鼠说的版本
huangmiao233
1 天前
有没有像 我这样的 go 里只会用 struct 连 interface 都很少用的...日常就是定义结构体. 写 function 好像也没啥..
assiadamo
1 天前
@huangmiao233 我类比就像把 package 级 function 当 static 用
leeonsoft
1 天前
用组合来代替继承,忘记继承
liuguang
1 天前
典型的 Java 思维, 现代语言已经不搞继承了,组合优于继承。
bunny189
1 天前
跟你说不清楚,典型的 Java 思维
https://i.imgur.com/TKfLzat_d.webp
bunny189
1 天前

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

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

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

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

© 2021 V2EX