我已经阅读过有关stackoverflow和其他网站上的文章的问题,但仍然无法解决问题。
这是我的代码:
package routing
import (
"net/http"
"bitbucket.org/codictive/ise/components/user"
)
// Route defines a component route structure.
type Route struct {
Path string
Name string
Method string
Description string
Handler func(w http.ResponseWriter, r *http.Request, data TemplateData)
}
// TemplateData defines data structure which passed to component handlers and rendered to client.
type TemplateData struct {
AppDomain string
Data map[string]interface{}
RequestPath string
Route Route
User *user.User
}
处理程序中使用的模板包的Render
函数将html模板呈现给客户端的浏览器:
package template
import (
"html/template"
"net/http"
"path"
"bitbucket.org/codictive/ise/components/log"
"bitbucket.org/codictive/ise/core/component/routing"
)
// Render executes given template to user's client with given data.
func Render(fp string, data routing.TemplateData, w http.ResponseWriter) {
files := []string{
"storage/templates/master.html",
path.Join("storage/templates", fp),
}
tmpl, err := template.New("master.html").Funcs(funcs).ParseFiles(files...)
if err != nil {
log.Error("[Render] Template creation failed. (%v)", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := tmpl.Execute(w, data); err != nil {
log.Error("[Render] Template execution failed. (%v)", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
每个组件都依赖于路由包来定义它的路由:
// Component defines application component structure.
type Component struct {
Name string
Config interface{}
Routes []routing.Route
// Boot filters
Before func()
After func()
}
和组件处理程序也使用routing.TemplateData
:
// indexHandler displays application homepage.
func indexHandler(w http.ResponseWriter, r *http.Request, data routing.TemplateData) {
}
所以代码高度依赖于routing.TemplateData
这是我的问题;因为TemplateData
还取决于routing.Route
和user.User
:
我无法为TemplateData
创建另一个包;它只是在那里移动进口周期。
另外我认为我不能使用接口,因为循环是在类型而不是函数上。
我无法将所有这些移到一个包中。
我不希望创建单一用途的软件包(例如user/models
,user/handlers
,...)尽可能绕过循环。
如何解决导入周期问题?
答案 0 :(得分:0)
这里有一些结构问题,所以你需要做一些改变。一些一般准则:
所以看看你的问题:
您的组件目前了解路由,但不需要 - 它们应该公开处理程序,您可以让应用程序或主程序包连接处理程序和路由器。处理程序不应该知道它们是如何使用的。
您的路由器知道模板和渲染,但不需要从那里删除TemplateData,处理程序应该通过导入模板包并使用它来渲染自己。
你的TemplateData知道用户但不必知道 - 我会删除它并将渲染上下文缩减为数据映射[string] interface {},因为用户可以添加到那里并让它分开并且添加很多,但确实意味着您的模板包必须知道您的特定用户类型,这是不好的。
前三个问题是删除不应存在的依赖关系 - 你的包太依赖了。
所以以开放的心态列出可能的布局解决方案(最好取决于项目的结构和复杂性):
您已经在某种程度上按资源分割(您称之为组件),所以让我们使用它。这是一个示例布局(每个条目都是一个包):
你说我不想创建单一目的包,你可能会觉得这违反了,但我建议你试试看,我认为它最接近你建议的结构。
这可以将用户的所有内容保存在一个位置,让页面处理程序根据需要提取用户或其他资源,而不会污染有关用户信息的页面 - 它会强制您保持组件完全独立,除了处理程序,允许拉入通常需要的多个组件。您可以找到此类结构here的示例。这意味着只有app和处理程序知道其他包,每个其他包都可以完全自包含(因此不会改变循环依赖性)。
好消息是,一旦你解决了这些问题,你就会发现你的软件包更加独立,这种约束(加快编译时间)是我最喜欢的Go之一,因为它也会迫使你相当分离的设计。