具有许多参数的构造函数

What's the most idiomatic way of initializing a Go type with many required parameters?

For example:

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
}

I want the Appointment type to be always valid; that is, I don't want to initialize it with a struct literal and then have to validate it.

Don't want:

a := &Appointment{
  Title: "foo",
  Details: "bar",
  StartingTime: 12451412,
  ...
}

err := a.Validate()

whats the best way to initialize this type of object (with lots of fields) without having to supply all the arguments in the constructor arguments?

One way you could avoid having to pass 10+ arguments to your constructors is to have an XxxParams type for each of your Xxx types and have your NewXxx take that params type as its argument. Then the NewXxx constructor would construct an Xxx value from those params, validate it, and return it, or an error, depending on the result of the validation.

This might feel redundant if you're constructing the XxxParams values manually as opposed to unmarshaling them from json, xml, etc.; but still, this way you are enforcing, however loosely, only valid Xxx's to be constructed, keeping the possibly invalid state in the input (XxxParams).

Here's an example from Stripe's repo: Account, AccountParams, and constructor

One pattern seen used by popular Go project is to create functions that return desired state of a struct. (checkout the httprouter project as an example - although its New func does not take any args...)

In your case - you could write a function that returns an Appointment with desired properties initialized.

for example

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

Then used in another file, import the package

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
}

Depending on how tight you want access control of the Appointment object property values, you do not have to export all of them (by setting them to lowercase) and provide more traditional accessor (think get, set) methods on the struct itself to ensure the struct always remain "valid"

I want the Appointment type to be always valid; that is, I don't want to initialize it with a struct literal and then have to validate it.

The only way to guarantee this is not to export the type. Then the only for a consumer of your package to obtain a struct of that type, is through your constructor method. Keep in mind that returning non-exported types is kind of ugly. One possible way around this is to access your data through an exported interface. That brings in a number of other considerations--which may be good, or bad for any given situation.

Now, while that's the only way to strictly meet your stated requirement, it may not actually be necessary. You might consider relaxing your requirement.

Consider this:

You must validate input data!

All you're deciding on is whether you're doing the validation at object creation time, or at object consumption time. Go's constructs generally make the latter easier (for both the coder, and the consumer of the data). If you truly must do validation at object creation time, then your only option is to use unexported types, and getter/setter methods for everything.

You may be able to use the "functional options" pattern to achieve this. It allows you to define functions for each input, removing the need for you to pass lots of options to your constructor.

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

The resulting call looks something like:

ap, err := appointment.New(
  AtEndTime(123),
  WithGuests([]Guest{...}),
)

If you want to validate each option in the function itself, it's not too much work to change that signature to possibly return an error too.