避免反射 - 我怎样才能最好地重构这段代码?
Avoiding reflection - How best can I refactor this code?
我开始尝试使用 Go,到目前为止效果非常好。我决定做一个小应用程序来帮助朋友整理他(小)公司中与业务相关的信息,我想我会用 Go 来实现它。
我还没有(确切地)运行遇到问题,这更像是一个问题,我什么时候应该考虑使用反射?例如,我有 3 个相关类型:Company
、Project
和 Staff
。它们都有几个共同的字段(例如id
、name
)所以你可以想象,从数据库加载它们的函数(我使用的是MySQL
)都非常相似。
查看 LoadCompany()
、LoadStaff()
和 LoadProject()
:
// Loads the company from the database with the given id.
func LoadCompany(id int) (Company, error) {
db := tools.OpenDB()
defer db.Close()
stmt, err := db.Prepare("SELECT * FROM companies WHERE id = ?")
if err != nil {
log.Panic(err)
}
var c Company
err = stmt.QueryRow(id).Scan(&c.id, &c.FullName, &c.Name, &c.History, &c.Overview, &c.Est, &c.Phone, &c.Website, &c.Email)
if err != nil {
return Company{}, err
}
return c, nil
}
// Loads the staff from the database with the given id.
func LoadStaff(id int) (Staff, error) {
db := tools.OpenDB()
defer db.Close()
stmt, err := db.Prepare("SELECT * FROM staff WHERE id = ?")
if err != nil {
log.Panic(err)
}
var s Staff
err = stmt.QueryRow(id).Scan(&s.id, &s.FullName, &s.Name, &s.Email, &s.Joined, &s.Left, &s.History, &s.Phone, &s.Position)
if err != nil {
return Staff{}, err
}
return s, nil
}
// Loads the project from the database with the given id.
func LoadProject(id int) (Project, error) {
db := tools.OpenDB()
defer db.Close()
stmt, err := db.Prepare("SELECT * FROM projects WHERE id = ?")
if err != nil {
log.Panic(err)
}
var p Project
err = stmt.QueryRow(id).Scan(&p.id, &p.Title, &p.Overview, &p.Value, &p.Started, &p.Finished, &p.Client, &p.Architect, &p.Status)
if err != nil {
return Project{}, err
}
return p, nil
}
当我写 LoadCompany()
时,我对自己感觉很好(ahem 作为一个 beginner/intermediate 程序员)因为它看起来很简洁。但是当我写 LoadStaff()
和 LoadProject()
时,我所做的只是复制和调整。我确信有更好的方法来做到这一点,但我厌倦了在阅读 Pike's post on it:
后跳入反思
[Reflection is] a powerful tool that should be used with care and avoided unless strictly necessary.
所以我的问题是,我应该使用反射吗?如果是的话,你能给我一些关于这种事情的最佳技术的建议吗?这只是冰山一角,因为我觉得与这些类型相关的其余函数和方法都类似地重复(并且不要让我开始测试!)。
谢谢!
类似于:
func LoadObject(sql string, id int, dest ...interface{}) error {
db := tools.OpenDB()
defer db.Close()
stmt, err := db.Prepare(sql)
if err != nil {
log.Panic(err)
}
defer stmt.Close()
return stmt.QueryRow(id).Scan(dest)
}
// Loads the company from the database with the given id.
func LoadCompany(id int) (c Company, err error) {
err = LoadObject("SELECT * FROM companies WHERE id = ?", &c.id,
&c.FullName, &c.Name, &c.History, &c.Overview, &c.Est, &c.Phone, &c.Website, &c.Email)
return
}
请注意,我还没有编译这段代码,但希望它足以给你一个想法。
几点建议:
- 通读文档:https://golang.org/pkg/database/sql/
- 在程序启动时创建
sql.DB
实例一次
- 在 SQL 语句中明确指定列顺序 (
select full_name, history, .... from companies ....
)
我开始尝试使用 Go,到目前为止效果非常好。我决定做一个小应用程序来帮助朋友整理他(小)公司中与业务相关的信息,我想我会用 Go 来实现它。
我还没有(确切地)运行遇到问题,这更像是一个问题,我什么时候应该考虑使用反射?例如,我有 3 个相关类型:Company
、Project
和 Staff
。它们都有几个共同的字段(例如id
、name
)所以你可以想象,从数据库加载它们的函数(我使用的是MySQL
)都非常相似。
查看 LoadCompany()
、LoadStaff()
和 LoadProject()
:
// Loads the company from the database with the given id.
func LoadCompany(id int) (Company, error) {
db := tools.OpenDB()
defer db.Close()
stmt, err := db.Prepare("SELECT * FROM companies WHERE id = ?")
if err != nil {
log.Panic(err)
}
var c Company
err = stmt.QueryRow(id).Scan(&c.id, &c.FullName, &c.Name, &c.History, &c.Overview, &c.Est, &c.Phone, &c.Website, &c.Email)
if err != nil {
return Company{}, err
}
return c, nil
}
// Loads the staff from the database with the given id.
func LoadStaff(id int) (Staff, error) {
db := tools.OpenDB()
defer db.Close()
stmt, err := db.Prepare("SELECT * FROM staff WHERE id = ?")
if err != nil {
log.Panic(err)
}
var s Staff
err = stmt.QueryRow(id).Scan(&s.id, &s.FullName, &s.Name, &s.Email, &s.Joined, &s.Left, &s.History, &s.Phone, &s.Position)
if err != nil {
return Staff{}, err
}
return s, nil
}
// Loads the project from the database with the given id.
func LoadProject(id int) (Project, error) {
db := tools.OpenDB()
defer db.Close()
stmt, err := db.Prepare("SELECT * FROM projects WHERE id = ?")
if err != nil {
log.Panic(err)
}
var p Project
err = stmt.QueryRow(id).Scan(&p.id, &p.Title, &p.Overview, &p.Value, &p.Started, &p.Finished, &p.Client, &p.Architect, &p.Status)
if err != nil {
return Project{}, err
}
return p, nil
}
当我写 LoadCompany()
时,我对自己感觉很好(ahem 作为一个 beginner/intermediate 程序员)因为它看起来很简洁。但是当我写 LoadStaff()
和 LoadProject()
时,我所做的只是复制和调整。我确信有更好的方法来做到这一点,但我厌倦了在阅读 Pike's post on it:
[Reflection is] a powerful tool that should be used with care and avoided unless strictly necessary.
所以我的问题是,我应该使用反射吗?如果是的话,你能给我一些关于这种事情的最佳技术的建议吗?这只是冰山一角,因为我觉得与这些类型相关的其余函数和方法都类似地重复(并且不要让我开始测试!)。
谢谢!
类似于:
func LoadObject(sql string, id int, dest ...interface{}) error {
db := tools.OpenDB()
defer db.Close()
stmt, err := db.Prepare(sql)
if err != nil {
log.Panic(err)
}
defer stmt.Close()
return stmt.QueryRow(id).Scan(dest)
}
// Loads the company from the database with the given id.
func LoadCompany(id int) (c Company, err error) {
err = LoadObject("SELECT * FROM companies WHERE id = ?", &c.id,
&c.FullName, &c.Name, &c.History, &c.Overview, &c.Est, &c.Phone, &c.Website, &c.Email)
return
}
请注意,我还没有编译这段代码,但希望它足以给你一个想法。
几点建议:
- 通读文档:https://golang.org/pkg/database/sql/
- 在程序启动时创建
sql.DB
实例一次 - 在 SQL 语句中明确指定列顺序 (
select full_name, history, .... from companies ....
)