jjzjj

go - 类型导入周期

coder 2024-07-09 原文

看了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 制作另一个包;它只是将导入周期移到那里。

  • 另外我认为我不能使用接口(interface),因为循环针对的是类型而不是函数。

  • 我无法将所有这些都放在一个包中。

  • 我不想创建单一用途的包(例如 user/modelsuser/handlers ...)只是为了绕过循环,因为可能。

如何解决导入周期问题?

最佳答案

这里的结构存在一些问题,因此您需要进行一些更改。一些一般准则:

  • 尽量让包了解世界的最低限度(只有少数应该导入很多其他的)
  • 在使用它们的 pkg 中声明数据结构或接口(interface)
  • 喜欢简单的接口(interface)(只有几个方法)和简单的数据结构
  • 尝试让包从不在它们上面导入包,只导入 sibling 或 child (并非总是可行,但这是一个很好的指导方针)

所以看看你的问题:

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

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

  3. 您的 TemplateData 知道用户但不必知道 - 我会删除它并将渲染上下文减少到 Data map[string]interface{},因为用户可以添加到那里并且将它分开不会'添加不多但确实意味着您的模板包必须了解您的特定用户类型,这很糟糕。

前三点是去除不应该存在的依赖的问题——你的包太相互依赖了。

  1. 最后,也是至关重要的,您的组件无法了解其他组件,否则您最终会遇到一个需要同时导入用户、页面和标签的处理程序,但不能,因为标签处理程序也需要导入用户,或者用户将有标签,标签将有用户等。要解决这个问题,您需要以某种方式将处理程序与组件分开。

所以以开放的心态列出可能的布局解决方案(最好的取决于项目的结构和复杂性):

  • 将所有内容放在一个包中(适合简单的应用程序,不适合复杂的应用程序)
  • 按功能(模型、 View 、处理程序等)拆分 - 我不太热衷于跳来跳去并拆分一些组件(例如用户)以将模型与处理程序分开 - 这是一种类似 rails 的方法。
  • 按资源/组件拆分,但对处理程序使用子包(我的首选选项)

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

  • server.go -(主要调用应用程序设置,运行服务器)
  • 应用程序(设置数据库、日志记录等,将处理程序链接到路由器,全部导入)
  • 用户(组件)
    • 处理程序(用户处理程序)
    • Assets (用户 Assets 、js等)
    • 模板(用户模板)
  • 页面
    • 处理人员
    • Assets
    • 模板 ...

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

这将关于用户的一切都放在一个地方,让页面处理程序根据需要拉入用户或其他资源,而不会用关于用户的信息污染页面说 - 它迫使你将组件完全分开,除了在处理程序中,允许拉入通常需要的多个组件。您可以找到此类结构的示例 here .这意味着只有应用程序和处理程序知道其他包,每个其他包都可以完全独立(因此循环依赖性没有变化)。

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

关于go - 类型导入周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46388701/

有关go - 类型导入周期的更多相关文章

  1. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  2. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  3. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  4. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  5. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  6. ruby - 检查是否通过 require 执行或导入了 Ruby 程序 - 2

    如何检查Ruby文件是否是通过“require”或“load”导入的,而不是简单地从命令行执行的?例如:foo.rb的内容:puts"Hello"bar.rb的内容require'foo'输出:$./foo.rbHello$./bar.rbHello基本上,我想调用bar.rb以不执行puts调用。 最佳答案 将foo.rb改为:if__FILE__==$0puts"Hello"end检查__FILE__-当前ruby​​文件的名称-与$0-正在运行的脚本的名称。 关于ruby-检查是否

  7. Ruby:如何从另一个文件导入变量? - 2

    我正在尝试创建一个与compass一起使用的本地配置文件,这样我们就可以处理开发人员机器上的不同导入路径。到目前为止,我已经尝试将文件导入到异常block中,以防它不存在,然后进一步使用该变量:local_config.rbVENV_FOLDER='venv'config.rbVENV_FOLDER='.'beginrequire'local_config.rb'rescueLoadErrorendputsVENV_FOLDER通常我是一名Python开发人员,所以我希望导入将VENV_FOLDER的值更改为venv,但它仍然是。之后。有没有一种方法可以导入local_config.r

  8. ruby-on-rails - Rails 迁移中的 PostgreSQL 点类型 - 2

    我想使用PostgreSQL中的point类型。我已经完成了:railsgmodelTestpoint:point最终的迁移是:classCreateTests当我运行时:rakedb:migrate结果是:==CreateTests:migrating====================================================--create_table(:tests)rakeaborted!Anerrorhasoccurred,thisandalllatermigrationscanceled:undefinedmethod`point'for#/hom

  9. ruby-on-rails - 我可以用鸭子类型(duck typing)改进这种方法吗? - 2

    希望我没有误解“ducktyping”的含义,但从我读到的内容来看,这意味着我应该根据对象如何响应方法而不是它是什么类型/类来编写代码。代码如下:defconvert_hash(hash)ifhash.keys.all?{|k|k.is_a?(Integer)}returnhashelsifhash.keys.all?{|k|k.is_a?(Property)}new_hash={}hash.each_pair{|k,v|new_hash[k.id]=v}returnnew_hashelseraise"CustomattributekeysshouldbeID'sorPropertyo

  10. ruby-on-rails - 如何在 ActionController::TestCase 请求中设置内容类型 - 2

    我试图像这样在我的测试用例中执行获取:request.env['CONTENT_TYPE']='application/json'get:index,:application_name=>"Heka"虽然,它失败了:ActionView::MissingTemplate:Missingtemplatealarm_events/indexwith{:handlers=>[:builder,:haml,:erb,:rjs,:rhtml,:rxml],:locale=>[:en,:en],:formats=>[:html]尽管在我的Controller中我有:respond_to:html,

随机推荐