模板对象字段实施

时间:2016-01-03 10:53:49

标签: templates struct go interface embedding

Go提供了很好的HTML模板功能,但对我而言,确保我的应用程序中的模板始终可以使用某些字段非常重要。一个很好的例子是标题字段,需要在每个HTML页面上显示。

鉴于以下几页:
Home
Register
Contact

我可能会为模板系统创建以下对象:
HomePage结构 RegisterPage结构 ContactPage struct

是否有建议的方法来确保每个页面的结构具有某些可用的字段?

理想情况下,我会通过多态实现这一点,但Go中没有正式支持。替代方案embedding似乎没有任何强制执行,即所有子结构可以嵌入父结构但不必必须

如果我没有清楚地表达我的问题,请告诉我。

1 个答案:

答案 0 :(得分:3)

执行模板不会对参数强制执行任何操作,Template.Execute()会接受interface{}类型的值。

您是创建HomePageRegisterPageContactPage结构的人。什么阻止您嵌入带有必填字段的BasePage结构?你担心你会忘记吗?你会在第一次测试时发现它,我不担心:

type BasePage struct {
    Title string
    Other string
    // other required fields...
}

type HomePage struct {
    BasePage
    // other home page fields...
}

type RegisterPage struct {
    BasePage
    // other register page fields...
}

如果您希望从代码中检查页面结构是否嵌入BasePage,我推荐另一种方式:接口。

type HasBasePage interface {
    GetBasePage() BasePage
}

实现它的示例HomePage

type HomePage struct {
    BasePage
    // other home page fields...
}

func (h *HomePage) GetBasePage() BasePage {
    return h.BasePage
}

现在显然只有GetBasePage()方法的网页可以作为HasBasePage的值传递:

var page HasBasePage = &HomePage{} // Valid, HomePage implements HasBasePage

如果您不想使用接口,可以使用reflect包来检查值是否为结构值以及是否嵌入了另一个接口。嵌入式结构出现并且可以像普通字段一样访问,例如使用Value.FieldByName(),类型名称为字段名称。

使用reflect检查值是否嵌入BasePage的示例代码:

page := &HomePage{BasePage: BasePage{Title: "Home page"}}

v := reflect.ValueOf(page)
if v.Kind() == reflect.Ptr {
    v = v.Elem()
}
if v.Kind() != reflect.Struct {
    fmt.Println("Error: not struct!")
    return
}
bptype := reflect.TypeOf(BasePage{})
bp := v.FieldByName(bptype.Name()) // "BasePage"
if !bp.IsValid() || bp.Type() != bptype {
    fmt.Println("Error: struct does not embed BasePage!")
    return
}

fmt.Printf("%+v", bp)

输出(在 Go Playground 上试试):

{Title:Home page Other:}