from abc import ABC, abstractmethod
class Device(ABC):
@abstractmethod
def fn(self):
raise NotImplementedError
def sync(self):
print("Start Sync ...")
self.fn()
print("Start Done.")
class PC(Device):
def fn(self):
print("PC Device fn!!!")
class Router(Device):
def fn(self):
print("Router Device sync")
def start_sync(d: Device):
d.sync()
if __name__ == '__main__':
p = PC()
r = Router()
start_sync(p) # Run p.fn()
start_sync(r) # Run r.fn()
请熟悉 Go 的大佬指导下,在 Go 中如何用接口与结构体实现类似的功能?(子类只需要重写 fn 方法,在 start_sync 处调用基类的 sync 时,能调用到子类重写后的 fn 方法)
1
FreeEx 2023-05-17 21:20:15 +08:00
golang 不支持方法重载。
https://go.dev/doc/faq#overloading > 为什么 Go 不支持方法和运算符的重载? > 如果方法分派也不需要进行类型匹配,则它会得到简化。使用其他语言的经验告诉我们,具有相同名称但不同签名的各种方法偶尔有用,但在实践中也可能令人困惑和脆弱。仅按名称匹配并要求类型一致是 Go 类型系统中的一个主要简化决策。 > 关于运算符重载,它似乎比绝对要求更方便。同样,没有它,事情会更简单。 |
2
Alias4ck 2023-05-17 21:29:44 +08:00
语言的转换 你问 chatgpt 可能一下就搞定了
|
3
chaleaochexist 2023-05-17 21:58:41 +08:00
|
4
Contextualist 2023-05-17 22:06:51 +08:00 1
Go 的 interface 和 struct 那一套鼓励的是组合 (composition) 而不是继承,所以像 PC 和 Router 这种实现具体功能的类是会被作为 Device 这种实现公共方法的类的**一部分**,就是 Device 包裹住 PC 或 Router 。下面是我个人认为的对应 Go 的地道解决办法: https://go.dev/play/p/wio55S6HtdK
|
5
lesismal 2023-05-17 22:14:20 +08:00
|
6
Lighthughjiajin OP @Contextualist 终于看到比较地道的写法,是我想了解的。
还有一点疑问,在这场景下,IDevice, Device 结构体、接口的命名有什么约定吗? |
7
Lighthughjiajin OP @Contextualist 这个写法下,有个缺点,Router 无法重写 Device 的 sync 方法,类似于以下 Python 代码
``` from abc import ABC, abstractmethod class Device(ABC): @abstractmethod def fn(self): raise NotImplementedError def sync(self): print("Start Sync ...") self.fn() print("Start Done.") class PC(Device): def fn(self): print("PC sync") class Router(Device): def fn(self): print("Router Device sync") def sync(self): print("Router 重写了 sync") super().sync() print("Router sync ...") def start_sync(d: Device): d.sync() if __name__ == '__main__': r = Router() start_sync(r) # Run r.fn() start_sync(PC()) ``` |
8
Contextualist 2023-05-17 22:47:54 +08:00
@Lighthughjiajin 嗯对,为了清晰对应你的例子我尽量沿用了你的命名。Go 里的 Interface 一般都是“动词 + er”来命名。假设我把 fn 重命名为 act ,那接口就叫 Actor 或者 DeviceActor ,对应的实现叫 PCActor 和 RouterActor 。
关于你提到的无法重写 sync 方法的问题,这意味着在你的业务逻辑里 sync 并不是只有一个实现,那就应该也把它列进接口里。根据不同情况,可以开一个新的 interface ,也可以加进 IDevice 。 - 开一个新的 interface 那就可以实现一个 type DefaultSyncer struct{},只是初始化 Device 时得传两个值 - 加进 IDevice 就是强制要求每一个具体实现都要实现 sync |
9
Lighthughjiajin OP |
10
Contextualist 2023-05-18 00:23:39 +08:00
@Lighthughjiajin 免大佬。基本没问题,就是注意一下依赖关系,Syncer 单方面依赖 Actor ,22 行是没必要的。
不过这个玩具例子有点过于复杂了😅,但愿你在实际应用中不会经常遇到这种情况。 |
11
Lighthughjiajin OP 我写的是组合多个结构体来满足函数的 Device 接口参数要求,我看你写的是组合多个结构体来调用 sync ,也就是最终都是同一个类型,就是一个结构体。
|