具有许多参数的构造函数

时间:2017-04-11 02:56:24

标签: go

使用许多必需参数初始化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()

什么是初始化这种类型的对象(包含大量字段)的最佳方法,而不必在构造函数参数中提供所有参数?

4 个答案:

答案 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的回购示例:AccountAccountParamsconstructor

答案 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方法。

相关问题