英文:
Go: Reflection catch22 reflect package
问题
好的,以下是翻译好的内容:
好的.. 我有一个名为mao的Web框架包,我正在编写它。
我想在控制器中定义我的路由。
在mao中:
type (
Controller struct {
Route Route
}
Route struct {
Name, Host, Path, Method string
}
)
在mao中导入包:
controller/default.go
type DefaultController struct {
mao.Controller
}
func (this *DefaultController) Index() Response {
this.Route = mao.Route{"default_index","localhost","/", "GET"}
}
现在,由于我想在控制器内定义我的路由,当实例化路由器时,它应该读取所有控制器。这就是问题所在。
我如何将包名传递给我的路由器,以便它能够获取该包中的所有结构体和函数?这是否可能?
英文:
Ok.. I have, or am writing a web framework package, called mao.
I'd like to have my Route defined in the controller.
in mao
type (
Controller struct {
Route Route
}
Route struct {
Name, Host, Path, Method string
}
)
in mao importing package
controller/default.go
type DefaultController struct {
mao.Controller
}
func (this *DefaultController) Index() Response {
this.Route = mao.Route{"default_index","localhost","/", "GET"}
}
Now since I'd like to define my route inside the controller, the router, when instanced should read all controllers. That's the problem.
How do I pass the package name to my router so it's able to get all structs and functions in that package? Is it even possible?
答案1
得分: 2
你所询问的在Go语言中是不可能的,因为它没有一种方法来枚举包/程序中的所有类型。
一个替代方案是参考database/sql
包的做法,当导入其他包时,可以让其他包向它注册。
例如,要在该包中使用PostgreSQL驱动程序,可以这样做:
import (
_ "github.com/lib/pq"
"database/sql"
)
...
db, err := sql.Open("postgres", "dbname=test")
在github.com/lib/pq
包的初始化过程中,PostgreSQL驱动程序被注册。以下是该包中相关的代码(省略了一些不相关的部分):
package pq
import (
"database/sql"
)
type drv struct{}
func (d *drv) Open(name string) (driver.Conn, error) {
return Open(name)
}
func init() {
sql.Register("postgres", &drv{})
}
也许你可以创建一个类似的注册API,以便在程序中找到各种可用的实现方式。
英文:
What you ask isn't possible in Go, since it doesn't have a way to enumerate all types in a package/program.
One alternative would be to follow the lead of the database/sql
package, and have a system where other packages can register with it when imported.
For example, to use the PostgreSQL driver with that package, you might do:
import (
_ "github.com/lib/pq"
"database/sql"
)
...
db, err := sql.Open("postgres", "dbname=test")
The postgres driver is registered during initialisation of the github.com/lib/pq
package. Here is the relevant code from that package (eliding some parts that aren't relevant):
package pq
import (
"database/sql"
)
type drv struct{}
func (d *drv) Open(name string) (driver.Conn, error) {
return Open(name)
}
func init() {
sql.Register("postgres", &drv{})
}
Perhaps you could create a registration API like this to find the various implementations available in the program?
答案2
得分: 0
老实说,我认为你的做法是错误的。
"自动注册"会使发生的事情变得模糊,导致代码难以测试和推理。
我建议将控制器(controller)作为一个接口,由具体的控制器来满足,并在路由器(router)上添加一个方法Add(c Controller)
,以将控制器分配给调用主项目的控制器(该项目导入了路由器和控制器)。这样可以使你的代码更易于理解和明确,并符合Go语言的精神。
database/sql
驱动程序的注册更像是一种技巧,不应被视为最佳实践。
英文:
Honestly, I think you are doing it the wrong way.
"auto registering" is obfuscating what happens and will lead to code that is hard to test and to reason about.
I would suggest to make controller an interface that should be satisfied by the concrete controllers
and have a method Add(c Controller)
on the router to assign the controller in the calling main project
(that imports the router and the controllers). This should make your code understandable and explicit
and is more in the spirit of go.
The database/sql
driver registration is more of a hack and should not be considered best practice.
答案3
得分: 0
我认为你应该为路由器创建一个结构体(可以是全局的,就像http.DefaultClient一样),然后在控制器的构造函数中,你可以将这个路由器作为依赖注入,以便为路由器注入相关的路由。使用依赖注入和接口可以使你的代码更加清晰和可测试,不仅在Go语言中如此。
这只是一个想法。
英文:
I think you should have one struct for router (which may well be global, like http.DefaultClient) and then in constructor functions for your controllers you'd be able to inject this router as a dependency so that a relevant route is injected for the router. DI+interfaces make your code nice and testable, not only in Go.
Just an idea.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论