英文:
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{}
类型的值。
你可以创建HomePage
、RegisterPage
和ContactPage
结构体。有什么阻止你将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:}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论