Golang接口最佳实践求助(新手向)
Golang接口最佳实践求助(新手向) 我定义了这样一个接口 FrontEnd:
package game
type FrontEnd interface {
PresentBoard(Board)
AwaitMove(Board) (byte, byte)
EndGame(board Board, winner Tic, moves byte)
}
我希望创建多个具有这三个方法的不同前端,例如 CLIFrontEnd。我的解决方案是创建一个空结构体,然后像这样添加方法:
package cli
type cliFrontEnd struct{}
func (cliFrontEnd) AwaitMove(board game.Board) (byte, byte) {
// ...
}
func (cliFrontEnd) PresentBoard(board game.Board) {
// ...
}
func (cliFrontEnd) EndGame(board game.Board, winner game.Tic, moves byte) {
// ...
}
然后在 cli 包中创建一个方法来生成 cli 前端:
func New() cliFrontEnd {
return cliFrontend{}
}
这是最好的实现方式吗?我感觉可能有更好的方法,例如我不太理解空结构体的意义。另外,New 方法是我应该采用的方式吗?看起来挺简洁的,这样我就可以在其他包中使用 cli.New(),但我不确定在 Go 语言中什么是规范的做法?
如果有什么不清楚的地方,请添加评论。
更多关于Golang接口最佳实践求助(新手向)的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你正在使用适配器设计模式,这没有对错之分。
我喜欢用接口类型来解决你在帖子中提出的这个问题。
示例在此:Go (GoLang) 中的适配器设计模式 - 欢迎来到 Golang By Example
我在 prestd 中使用了这种模式,具体位置在此:prest/adapters at main · prest/prest · GitHub
更多关于Golang接口最佳实践求助(新手向)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中,你的实现方式是完全正确的,这是接口实现的典型模式。空结构体cliFrontEnd{}在内存中占用零字节,当类型不需要维护状态时,这是最简洁的实现方式。
示例代码:
// game/game.go
package game
type Tic byte
type Board [9]Tic
type FrontEnd interface {
PresentBoard(Board)
AwaitMove(Board) (byte, byte)
EndGame(board Board, winner Tic, moves byte)
}
// cli/cli.go
package cli
import "game"
type cliFrontEnd struct{}
func (cliFrontEnd) PresentBoard(board game.Board) {
for i, v := range board {
if i%3 == 0 {
println()
}
fmt.Printf("%c ", v)
}
println()
}
func (cliFrontEnd) AwaitMove(board game.Board) (byte, byte) {
var row, col byte
fmt.Print("Enter row and column (0-2): ")
fmt.Scan(&row, &col)
return row, col
}
func (cliFrontEnd) EndGame(board game.Board, winner game.Tic, moves byte) {
if winner == 0 {
fmt.Println("Game ended in a draw!")
} else {
fmt.Printf("Player %c wins in %d moves!\n", winner, moves)
}
}
// 导出接口类型而不是具体类型
func New() game.FrontEnd {
return cliFrontEnd{}
}
// main.go
package main
import (
"game"
"cli"
)
func main() {
var fe game.FrontEnd = cli.New()
var board game.Board
fe.PresentBoard(board)
row, col := fe.AwaitMove(board)
println(row, col)
}
关于New()函数,返回接口类型game.FrontEnd比返回具体类型cliFrontEnd更好,这样隐藏了实现细节。调用方只需要知道它获得了满足FrontEnd接口的对象。
如果你需要维护状态,可以这样实现:
package cli
import "game"
type cliFrontEnd struct {
playerName string
debugMode bool
}
func New(playerName string, debug bool) game.FrontEnd {
return &cliFrontEnd{
playerName: playerName,
debugMode: debug,
}
}
func (c *cliFrontEnd) PresentBoard(board game.Board) {
if c.debugMode {
fmt.Printf("Player: %s\n", c.playerName)
}
// ... 展示棋盘逻辑
}
这种模式在标准库中广泛使用,比如io.Reader接口的实现:
// 类似你的空结构体模式
type devNull struct{}
func (devNull) Write(p []byte) (int, error) { return len(p), nil }
func (devNull) Read(p []byte) (int, error) { return 0, io.EOF }
// 工厂函数返回接口
func NewNullDevice() io.ReadWriter {
return devNull{}
}
你的实现已经遵循了Go的最佳实践。

