使用许多必需参数初始化Go类型最常用的方法是什么?
例如:
type Appointment struct {
Title string
Details string
Dresscode string
StartingTime int64
EndingTime int64
RSVPdate int64
Place *Place
Guests []*Guest
}
type Place struct {
Name string
Address string
}
type Guest struct {
Name string
Status string
}
我希望Appointment
类型始终有效;也就是说,我不想用struct literal初始化它,然后必须验证它。
不想要:
a := &Appointment{
Title: "foo",
Details: "bar",
StartingTime: 12451412,
...
}
err := a.Validate()
什么是初始化这种类型的对象(包含大量字段)的最佳方法,而不必在构造函数参数中提供所有参数?
答案 0 :(得分:5)
您可以使用“functional options”模式来实现此目的。它允许您为每个输入定义函数,从而无需将大量选项传递给构造函数。
func New(options ...func(*Appointment)) (*Appointment, error) {
ap := &Appointment{
Title: "Set your defaults",
Details: "if you don't want zero values",
StartingTime: 123,
}
for _, option := range options {
option(ap)
}
// Do any final validation that you want here.
// E.g. check that something is not still 0 value
if ap.EndTime == 0 {
return nil, errors.New("invalid end time")
}
return ap, nil
}
// Then define your option functions
func AtEndTime(endTime int64) func(*Appointment) {
return func(ap *Appointment) {
ap.EndTime = endTime
}
}
结果调用类似于:
ap, err := appointment.New(
AtEndTime(123),
WithGuests([]Guest{...}),
)
如果要验证函数本身中的每个选项,那么将该签名更改为可能也会返回错误并不是太多工作。
答案 1 :(得分:2)
您可以避免向构造函数传递10个以上参数的一种方法是为每个XxxParams
类型设置Xxx
类型,并让NewXxx
将该参数类型设置为它的论点。然后NewXxx
构造函数将从这些参数构造Xxx
值,验证它,并返回它,或者根据验证结果返回错误。
如果你手动构造XxxParams
值而不是从json,xml等解组它们,这可能会有多余。但是,通过这种方式,你可以强制执行,只有松散,只能构造有效的Xxx
,在输入中保持可能无效的状态(XxxParams
)。
以下是Stripe的回购示例:Account,AccountParams和constructor
答案 2 :(得分:0)
流行的Go项目使用的一种模式是创建返回结构所需状态的函数。 (以httprouter项目结帐为例 - 虽然它的New
func不带任何参数......)
在您的情况下 - 您可以编写一个返回Appointment
的函数,并初始化所需的属性。
例如
package appointment
type Appointment struct {
//your example code here...
}
func New(title, details, dressCode string) *Appointment {
return &Appointment{
Title: "foo",
Details: "bar",
StartingTime: 12451412,
//set the rest of the properties with sensible defaults - otherwise they will initialize to their zero value
}
}
然后在另一个文件中使用,导入包
package main
import path/to/appointment
func main() {
myApt := appointment.New("Interview", "Marketing Job", "Casual")
//myApt is now a pointer to an Appointment struct properly initialized
}
根据您对Appointment对象属性值的访问控制的紧密程度,您不必导出所有这些(通过将它们设置为小写)并在struct本身上提供更传统的访问器(思考get,set)方法确保结构始终保持"有效"
答案 3 :(得分:0)
我希望约会类型始终有效;也就是说,我不想用struct literal初始化它,然后必须验证它。
保证这一点的唯一方法是不导出类型。然后,只有您的包的使用者才能获得该类型的结构,这是通过您的构造方法。请记住,返回非导出类型有点难看。解决此问题的一种可能方法是通过导出的界面访问您的数据。这带来了许多其他考虑因素 - 这可能是好的,或对任何给定的情况都不好。
现在,虽然这是严格满足您所述要求的唯一方法,但实际上可能没有必要。您可以考虑放宽您的要求。
考虑一下:
您决定的是您是在对象创建时还是在对象消耗时进行验证。 Go的构造通常使后者更容易(对于编码器和数据的消费者)。如果您真的必须在对象创建时进行验证,那么您唯一的选择是使用未导出的类型和getter / setter方法。