5

I have a WorkDay struct holding data about the times someone worked, a WorkWeek struct to hold a bunch of WorkDays, and a WorkMonth struct to hold a bunch of WorkWeeks. The idea is to have each return the total hours worked during that period.

type WorkDay struct {
    StartTime time.Time
    EndTime   time.Time
}

type WorkWeek struct {
    WorkDays []WorkDay
}

type WorkMonth struct {
    WorkWeeks []WorkWeek
}

func (w WorkDay) HoursWorked() time.Duration {
    // Find hours worked through simple subtraction.
}

func (w WorkWeek) HoursWorked() time.Duration {
    var totalHours time.Duration
    for _, day := range w.WorkDays {
        totalHours += day.HoursWorked()
    }
    return totalHours
}

func (w WorkMonth) HoursWorked() time.Duration {
    var totalHours time.Duration
    for _, week := range w.WorkWeeks {
        totalHours += week.HoursWorked()
    }
    return totalHours
}

This code works just fine, but the duplication in WorkWeek.HoursWorked() and WorkMonth.HoursWorked() really grinds my gears. I tried to do the following, thinking I was very clever:

func (w WorkWeek) HoursWorked() time.Duration {
    return sumHoursWorked(w.WorkDays)
}

func (m WorkMonth) HoursWorked() time.Duration {
    return sumHoursWorked(m.WorkWeeks)
}

type countable interface {
    HoursWorked() time.Duration
}

func sumHoursWorked(timeFrames []countable) time.Duration {
    var totalHours time.Duration
    for _, frame := range timeFrames {
        totalHours += frame.HoursWorked()
    }
    return totalHours
}

However, as explained here, even though WorkDay implements countable, a slice of WorkDays does not count as a slice of countables.

So, is there some nifty, idiomatic way out of this situation that I'm missing, or am I just stuck with the duplication?

Community
  • 1
  • 1
sozorogami
  • 63
  • 5
  • How do you deal with fiscal months contain a variable number of often incomplete fiscal weeks? It seems like WorkMonth directly containing []WorkDay, and some method to extract weeks if necessary, would be a simpler implementation. – krait May 25 '14 at 22:12
  • You're right. This program was mostly for practice, but if I were going to go further with it, I'd probably rethink my types, as Volker suggested below. – sozorogami May 27 '14 at 07:43

2 Answers2

4

No. Either have a slice of countables which gives you dynamic method dispatch or some programming (your first solution) or restructure your types. I have no idea of your problem domain but months consisting of weeks consisting of days seems odd, at least the month/week stuff.

Volker
  • 40,468
  • 7
  • 81
  • 87
2

No because slice of countables is another type. You could define your own slice type and attach an Add method to it.

func (w WorkWeek) HoursWorked() time.Duration {
    return sumHoursWorked(w.WorkDays)
}

func (m WorkMonth) HoursWorked() time.Duration {
    return sumHoursWorked(m.WorkWeeks)
}

type countable interface {
    HoursWorked() time.Duration
}

type SliceCountable []countable

func (m *SliceCountable) Add( c countable ) {
   *m = append(*m, c ) 
}

func (m SliceCountable) HoursWorked() time.Duration {
var totalHours time.Duration
for _, frame := range m {
    totalHours += frame.HoursWorked()
}
return totalHours
}


func sumHoursWorked(timeFrames []countable) time.Duration {
    var totalHours time.Duration
    for _, frame := range timeFrames {
        totalHours += frame.HoursWorked()
    }
    return totalHours
}
fabrizioM
  • 46,639
  • 15
  • 102
  • 119