英文:
Best practice to partially implement an interface
问题
部分提供默认实现的go方式是什么?
为了说明这一点,以下是一个简单的切换开关驱动程序的示例,这是我按照面向对象的直觉得出的死胡同...当然它不能编译(我知道为什么),而且我不一定愿意这样做。任何其他更适合go哲学的解决方案实际上都更好,以便正确理解这个常见需求的go方式。
完整的示例也可以在https://play.golang.org/p/MYED1PB-dS找到
给定以下接口:
type ToggleSwitch interface {
TurnOn()
TurnOff()
IsOn() bool
Toggle()
}
Toggle()
是一个很好的候选项,可以提供默认实现(即根据当前状态打开或关闭开关):
// Toggle()方法已经可以使用TurnOn、TurnOff()和IsOn()进行定义。
type DefaultDriver struct {
}
// 对于非优化的情况,以下实现是可以的:
func (d *DefaultDriver) Toggle() {
state := d.IsOn()
fmt.Println("generic toogle ->", state)
if state {
d.TurnOff()
} else {
d.TurnOn()
}
}
然后,实际的驱动程序可以使用它,也可以不使用它:
// 实际的ToggleDriver示例,应根据默认实现或其他方式完全实现接口。
// 例如,如果切换开关设备具有内置的切换命令,则可以将Toggle()方法优化为直接使用它。
type DummyDriver struct {
*DefaultDriver // 提升DefaultDriver方法
state bool
}
func (d *DummyDriver) IsOn() bool {
return d.state
}
func (d *DummyDriver) TurnOff() {
d.state = false
}
func (d *DummyDriver) TurnOn() {
d.state = false
}
// 取消对我进行优化...
//func (d *DummyDriver) Toggle() {
// fmt.Println("specialized toogle ->", d.state)
// d.state = !d.state
//}
英文:
What would be the go-way of partly providing a default implementation?
To illustrate, the following simple example of a toggle switch driver is the dead-end I ended with by following my OO intuition... Of course it does not compile (I know why) and I am not necessarily willing to do so. Any other solution better fitting into the go philosophy would be in fact even better to correctly understand the go-way for this common need.
The complete example can also be found at https://play.golang.org/p/MYED1PB-dS
Given the following interface:
type ToggleSwitch interface {
TurnOn()
TurnOff()
IsOn() bool
Toggle()
}
Toggle()
is a good candidate to be provided a default implementation (ie, according to the current state, turn on or off the switch):
// The Toggle() method can already be defined using TurnOn, TurnOff() and IsOn().
type DefaultDriver struct {
}
// The following implementation would be fine for non-optimized cases:
func (d *DefaultDriver) Toggle() {
state := d.IsOn()
fmt.Println("generic toogle ->", state)
if state {
d.TurnOff()
} else {
d.TurnOn()
}
}
And then an actual driver could use it or not:
// Example of an actual ToggleDriver which should fully implement the interface
// based on the default implementation or not.
// For example, if the toggle switch device has a bult-in toggle command, the
// Toggle() method could be optimized to directly use it.
type DummyDriver struct {
*DefaultDriver // promote DefaultDriver methods
state bool
}
func (d *DummyDriver) IsOn() bool {
return d.state
}
func (d *DummyDriver) TurnOff() {
d.state = false
}
func (d *DummyDriver) TurnOn() {
d.state = false
}
// Uncomment me to optimize me...
//func (d *DummyDriver) Toggle() {
// fmt.Println("specialized toogle ->", d.state)
// d.state = !d.state
//}
答案1
得分: 12
个人而言,我会为DefaultDriver类型实现Toggle、IsOn、TurnOn和TurnOff方法,以满足ToggleSwitch接口的要求。
然后,DummyDriver类型将嵌入DefaultDriver类型。
这样,您可以根据需要为DummyDriver类型实现特定的方法。
结果可能如下所示:
package main
import "fmt"
type ToggleSwitch interface {
TurnOn()
TurnOff()
IsOn() bool
Toggle()
}
type DefaultDriver struct {
state bool
}
func (d *DefaultDriver) Toggle() {
state := d.IsOn()
fmt.Println("generic toggle ->", state)
if state {
d.TurnOff()
} else {
d.TurnOn()
}
}
func (d *DefaultDriver) IsOn() bool {
return d.state
}
func (d *DefaultDriver) TurnOff() {
d.state = false
}
func (d *DefaultDriver) TurnOn() {
d.state = true
}
type DummyDriver struct {
DefaultDriver
state bool
}
// Uncomment me to optimize me...
//func (d *DummyDriver) Toggle() {
// fmt.Println("specialized toggle ->", d.state)
// d.state = !d.state
//}
func main() {
d := DummyDriver{state: false}
d.Toggle()
d.Toggle()
d.Toggle()
}
您可以在此链接中查看代码:https://play.golang.org/p/Xm-8A0xoRb
英文:
Personnally, I would implement the Toggle, IsOn, TurnOn and TurnOff methods for the DefaultDriver type, so it would satisfy the ToggleSwitch interface.
Then, the DummyDriver type would embed the DefaultDriver type.
This way, you could implement the specialized methods for the DummyDriver type, as you desire.
The result would be something along these lines:
package main
import "fmt"
type ToggleSwitch interface {
TurnOn()
TurnOff()
IsOn() bool
Toggle()
}
type DefaultDriver struct {
state bool
}
func (d *DefaultDriver) Toggle() {
state := d.IsOn()
fmt.Println("generic toogle ->", state)
if state {
d.TurnOff()
} else {
d.TurnOn()
}
}
func (d *DefaultDriver) IsOn() bool {
return d.state
}
func (d *DefaultDriver) TurnOff() {
d.state = false
}
func (d *DefaultDriver) TurnOn() {
d.state = true
}
type DummyDriver struct {
DefaultDriver
state bool
}
// Uncomment me to optimize me...
//func (d *DummyDriver) Toggle() {
// fmt.Println("specialized toogle ->", d.state)
// d.state = !d.state
//}
func main() {
d := DummyDriver{state: false}
d.Toggle()
d.Toggle()
d.Toggle()
}
答案2
得分: 6
在我看来,你正在尝试做的事情并不符合Go语言的风格。
ToggleSwitch
定义了四个方法,明显是在处理某种状态。为了为任何这些方法提供实现,你还需要实现那个状态(即使该状态不存在,例如通过将这些方法定义为空操作),但在这一点上,不提供所有这些方法就没有意义。
按照Go语言的类型组合方式,嵌入的类型通常应该是“完整的”,独立的,并且本身就是有用的。嵌入类型提供的方法只能在该字段上工作,无法访问“父级”或其方法。
如果 ToggleSwitch
还有其他与该状态无关的方法,那么只提供接口的部分实现就是有意义的,但在这一点上,一个更好、更符合惯例的解决方案是将 ToggleSwitch
定义为两个独立接口的组合。
换句话说,我认为在Go语言中没有一种“Go方式”来提供接口的部分实现(不是由多个接口组成),因为在Go语言中,习惯上会尽量定义尽可能小的接口。
英文:
In my opinion what you're trying to do isn't very Go-like.
ToggleSwitch
defines four methods that clearly work on some state. In order to provide an implementation for any of these methods, you also need to implement that state (even if that state's non-existent, e.g. by defining those methods as no-ops), but at that point, it simply doesn't make sense not to provide all those methods.
With Go-like type composition the embedded types should generally be "whole", complete and useful on their own. Methods provided by the embedded type only work on that field alone, there's no way to get to the "parent" or its methods.
If ToggleSwitch
also had other methods that didn't deal with that state, then it'd make sense to provide only a partial implementation of the interface, but at that point an even better and much more idiomatic solution would be to define ToggleSwitch
as a combination of two separate interfaces.
In other words, I don't think there's a "Go way" to provide a partial implementation of an interface (that's not composed of several interfaces), because it's idiomatic in Go to define interfaces that are as small as possible.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论