类型

时间:2017-09-24 09:45:46

标签: go types import importerror

我已经阅读过有关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.Routeuser.User

  • 我无法为TemplateData创建另一个包;它只是在那里移动进口周期。

  • 另外我认为我不能使用接口,因为循环是在类型而不是函数上。

  • 我无法将所有这些移到一个包中。

  • 我不希望创建单一用途的软件包(例如user/modelsuser/handlers,...)尽可能绕过循环。

如何解决导入周期问题?

1 个答案:

答案 0 :(得分:0)

这里有一些结构问题,所以你需要做一些改变。一些一般准则:

  • 尝试让包知道世界上最小的(只有少数应该导入很多其他的)
  • 在pkg中声明使用它们的数据结构或接口
  • 首选简单的界面(只有几种方法)和简单的数据结构
  • 尝试让软件包永远不会在它们之上导入包,只有兄弟姐妹或孩子(不总是可行的,但是一个很好的指导方针)

所以看看你的问题:

  1. 您的组件目前了解路由,但不需要 - 它们应该公开处理程序,您可以让应用程序或主程序包连接处理程序和路由器。处理程序不应该知道它们是如何使用的。

  2. 您的路由器知道模板和渲染,但不需要从那里删除TemplateData,处理程序应该通过导入模板包并使用它来渲染自己。

  3. 你的TemplateData知道用户但不必知道 - 我会删除它并将渲染上下文缩减为数据映射[string] interface {},因为用户可以添加到那里并让它分开并且添加很多,但确实意味着您的模板包必须知道您的特定用户类型,这是不好的。

  4. 前三个问题是删除不应存在的依赖关系 - 你的包太依赖了。

    1. 最后,至关重要的是,您的组件无法了解其他组件,或者您最终会遇到需要将用户,页面,标签全部导入到一起的Handler,因为标签处理程序还需要导入用户,或者用户将拥有标签和标签将拥有用户等。要解决这个问题,您需要以某种方式将处理程序与组件分开。
    2. 所以以开放的心态列出可能的布局解决方案(最好取决于项目的结构和复杂性):

      • 将所有内容放在一个软件包中(适用于简单的应用程序,不适合复杂的应用程序)
      • 按功能划分(模型,视图,处理程序等) - 我不是那么热衷于跳转并将像用户一样的组件拆分为与处理程序分开的模型 - 这是类似于轨道的方法。
      • 按资源/组件拆分,但使用子包处理程序(我的首选选项)

      您已经在某种程度上按资源分割(您称之为组件),所以让我们使用它。这是一个示例布局(每个条目都是一个包):

      • server.go - (主要调用应用程序设置,运行服务器)
      • app(设置数据库,记录等,将处理程序链接到路由器,全部导入)
      • 用户(组件)
        • 处理程序(用户处理程序)
        • 资产(用户资产,js等)
        • 模板(用户模板)
        • 处理程序
        • assets
        • 模板 ...

      你说我不想创建单一目的包,你可能会觉得这违反了,但我建议你试试看,我认为它最接近你建议的结构。

      这可以将用户的所有内容保存在一个位置,让页面处理程序根据需要提取用户或其他资源,而不会污染有关用户信息的页面 - 它会强制您保持组件完全独立,除了处理程序,允许拉入通常需要的多个组件。您可以找到此类结构here的示例。这意味着只有app和处理程序知道其他包,每个其他包都可以完全自包含(因此不会改变循环依赖性)。

      好消息是,一旦你解决了这些问题,你就会发现你的软件包更加独立,这种约束(加快编译时间)是我最喜欢的Go之一,因为它也会迫使你相当分离的设计。