Golang中这样的设计难道不让人困惑吗?

Golang中这样的设计难道不让人困惑吗? 让我们来看这段代码。在第一种情况下driver是一个包,而在第二种情况下db是一个对象,这难道不令人困惑吗?

当然,你可能会说在编码时你知道什么是对象、什么是包。没错,在我编写代码时可能是这样。但过段时间后,其他人打开这个项目时就得开始猜测这些分别是什么。

例如在Java中就不存在这种不明确性,因为var.do()这种表示法总是意味着var是一个对象。

var db *sql.DB

func main() {
	db = driver.ConnectDB()
	defer db.Close()
}
8 回复

当然导入了,但没有显示在那里。

更多关于Golang中这样的设计难道不让人困惑吗?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


那么它不是一个"对象"而是一个包。

这是一个包

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()是该类型的方法

这种设计有明确的语法规则:

  1. pkg.Func() - 包名+函数调用
  2. 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是对象,不存在需要猜测的情况。

回到顶部