模板对象字段强制执行

huangapple go评论89阅读模式
英文:

Template object field enforcement

问题

Go提供了强大的内置HTML模板功能,但对我来说,确保模板中的某些字段始终可用非常重要。一个很好的例子是标题字段,它需要在每个HTML页面上显示。

给定以下页面:
Home
Register
Contact

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

是否有一种推荐的方法来确保每个页面的结构体都具有某些可用字段?

理想情况下,我希望通过多态来实现这一点,但Go并不官方支持多态。作为替代,嵌入似乎没有任何强制性,即所有子结构体可以嵌入父结构体,但不一定必须

如果我没有表达清楚,请告诉我。

英文:

Go provides great build in HTML templating functionality, however for me it is important that I can ensure certain fields will always be available to templates in my application. A good example of this is a title field, which needs to be shown on every HTML page.

Given the following pages:
Home
Register
Contact

I would likely create the following objects for the templating system:
HomePage struct
RegisterPage struct
ContactPage struct

Is there a recommended way to ensure that the structs for each page have certain fields available to them?

Ideally I would implement this through Polymorphism, but that isn't officially supported in Go. The alternative, embedding doesn't appear to have any enforcement, i.e all of the child structs can embed a parent struct but don't have to.

Let me know if I haven't expressed my question clearly enough.

答案1

得分: 3

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

你可以创建HomePageRegisterPageContactPage结构体。有什么阻止你将BasePage结构体嵌入其中并添加所需的字段呢?你担心会忘记吗?你会在第一次测试时注意到它,我不会担心这个问题:

type BasePage struct {
    Title string
    Other string
    // 其他必需的字段...
}

type HomePage struct {
    BasePage
    // 其他首页字段...
}

type RegisterPage struct {
    BasePage
    // 其他注册页面字段...
}

如果你想通过代码检查页面结构体是否嵌入了BasePage,我建议使用另一种方法:接口。

type HasBasePage interface {
    GetBasePage() BasePage
}

下面是实现了该接口的HomePage示例:

type HomePage struct {
    BasePage
    // 其他首页字段...
}

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

现在,只有具有GetBasePage()方法的页面才能作为HasBasePage的值传递:

var page HasBasePage = &HomePage{} // 有效,HomePage实现了HasBasePage接口

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

以下是使用reflect检查一个值是否嵌入了BasePage的示例代码:

page := &HomePage{BasePage: BasePage{Title: "首页"}}

v := reflect.ValueOf(page)
if v.Kind() == reflect.Ptr {
    v = v.Elem()
}
if v.Kind() != reflect.Struct {
    fmt.Println("错误:不是结构体!")
    return
}
bptype := reflect.TypeOf(BasePage{})
bp := v.FieldByName(bptype.Name()) // "BasePage"
if !bp.IsValid() || bp.Type() != bptype {
    fmt.Println("错误:结构体未嵌入BasePage!")
    return
}

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

输出结果(在Go Playground上尝试):

{Title:首页 Other:}
英文:

Executing a template does not enforce anything to the parameters, Template.Execute() accepts a value of type interface{}.

You are the one creating the HomePage, RegisterPage and ContactPage structs. What stops you from embedding a BasePage struct with the required fields? Are you worried you will forget about it? You will notice it at the first testing, I wouldn't worry about that:

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...
}

If you want from code to check if page structs embed the BasePage, I recommend another way: interfaces.

type HasBasePage interface {
	GetBasePage() BasePage
}

Example HomePage that implements it:

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

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

Now obviously only pages that have a GetBasePage() method can be passed as a value of HasBasePage:

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

If you don't want to use interfaces, you can use the reflect package to check if a value is a struct value and if it embeds another interface. Embedded structs appear and can be accessed like ordinary fields e.g. with Value.FieldByName(), with the type name being the field name.

Example code using reflect to check if a value embeds 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)

Output (try it on the Go Playground):

{Title:Home page Other:}

huangapple
  • 本文由 发表于 2016年1月3日 18:53:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/34575704.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定