Golang中这样的设计难道不让人困惑吗?
Golang中这样的设计难道不让人困惑吗?
让我们来看这段代码。在第一种情况下driver是一个包,而在第二种情况下db是一个对象,这难道不令人困惑吗?
当然,你可能会说在编码时你知道什么是对象、什么是包。没错,在我编写代码时可能是这样。但过段时间后,其他人打开这个项目时就得开始猜测这些分别是什么。
例如在Java中就不存在这种不明确性,因为var.do()这种表示法总是意味着var是一个对象。
var db *sql.DB
func main() {
db = driver.ConnectDB()
defer db.Close()
}
那么它不是一个"对象"而是一个包。
这是一个包
package driver
import ( ...
db 是指向 sql.DB 类型的指针。不过我不确定 driver 是从哪里来的,你既没有导入它,也没有声明它。
另一个示例
router := mux.NewRouter()
controller := controllers.Controller{}
概念上的区别在于()和{}。它们看起来非常相似,但在某种意义上却完全不同。这也相当令人困惑。
Java 在完全不同的概念中也使用点号。
String cn = Customer.name // 类字段
String s = customer.name // 对象字段
String n = customer.getName() // 方法
import customer.name // 包
new customer.Info // 类
实际上这并不会造成问题,因为上下文信息能让你分辨出具体指的是哪种用法。
Jon,我主要的不满在于Go语言对完全不同的概念使用了相似的语法结构。例如在Java中,我们使用"点"符号的情况:
Element element = (Element) nodes.item(i);
NodeList title = element.getElementsByTagName("title");
Element line = (Element) title.item(0);
你觉得这里的共同点是什么?是的,就是"点"后面跟着的是方法。那么点前面是什么呢?是某个对象。这很清晰。 但在Golang中就不那么直观了:
db = driver.ConnectDB()
defer db.Close()
这里也有方法(准确说是函数),但在一种情况下我们在函数前使用包名,在下一行却使用对象名。所以我们用相似的语法结构表达了不同的含义。明白我的意思吗?
我之前提到的第二个例子:
router := mux.NewRouter()
controller := controllers.Controller{}
这里的共同点是什么?是的,都是使用某些操作(即"无返回值"的操作)。我们这样阅读:好的,这里有个mux包,我们使用了其中的某个函数…好吧…我们得到了某个路由器,好的…接着我们阅读下一行,开头看起来很相似:我们通过调用controllers对象(或者是包?!是的,我知道这是包…)上的某个"操作"来获取控制器,但是等等…这里完全是不同的概念,因为有花括号…好吧…你体会到阅读这段代码时的困惑了吗?
在Go语言中,包和对象的使用方式确实与Java等语言不同,但这源于Go的设计哲学和语法规则,并不代表它令人困惑。Go通过大小写区分包级导出和对象方法,代码结构清晰可预测。
在您的示例中:
driver.ConnectDB():driver是包名,ConnectDB是该包导出的函数(首字母大写)db.Close():db是*sql.DB类型的对象,Close()是该类型的方法
这种设计有明确的语法规则:
pkg.Func()- 包名+函数调用obj.Method()- 对象+方法调用
示例代码展示这种区别:
package main
import (
"database/sql"
"fmt"
"yourproject/driver" // 自定义包
)
var db *sql.DB
func main() {
// 包函数调用
db = driver.ConnectDB()
// 对象方法调用
err := db.Ping()
if err != nil {
fmt.Println("Database connection failed:", err)
}
defer db.Close()
// 另一个包函数调用示例
result := driver.ValidateConnection(db)
fmt.Println("Validation result:", result)
}
在driver包中:
package driver
import "database/sql"
// 包级导出函数
func ConnectDB() *sql.DB {
// 连接数据库的实现
db, _ := sql.Open("mysql", "connection_string")
return db
}
// 另一个包级函数
func ValidateConnection(db *sql.DB) bool {
return db.Ping() == nil
}
Go的这种设计强制开发者通过命名约定来明确代码结构:包提供工具函数,对象封装状态和行为。阅读代码时,通过导入语句就能知道driver是包,通过变量声明知道db是对象,不存在需要猜测的情况。

