有没有推荐的方法来定义切片集合的常见行为?

I am trying to get suggestion from the community in order to make the best practices. Please bear with me, with the following example:

Suppose that you work with half open intervals, that is, something that you know when it starts.

For example

  • There can be HalfOpenInterval restricted to a day. Example: you say "from 1:00 pm afterwards" (until the end of the day). Let's call it ClockInterval
  • There can be HalfOpenInterval restricted to the existence of universe. Example: you say "from 9 of july of 1810 we declare the indepedency" (until the end of the cosmos.. hypothetically). Let's call it Period

For both entities types: you work with a collection of them, so you usually have slices of clocks and periods in your code.

So now comes the problem: you must find the enclosing interval for a given time (func FindEnclosingHalfOpenInterval) for both clocks and periods, so you start writing the code...

And well, i get into this matter... how i should organize the code in order to write only once the common func. (func FindEnclosingHalfOpenInterval).

So i get into this code: https://play.golang.org/p/Cy7fFaFzYJR

But i keep wondering if there is a better way to define common behaviour for collection of slices.

Please reader you shall realize that i need to do an "element by element" conversion for each type of slice (and i have a type of slice for each type of concrete HalfOpenInterval i define). So i wonder if there is there any way that allows me to introduce new types of HalfOpenInterval without having to do some adjustement and "automatically" gets the abilitiy to use the func FindEnclosingHalfOpenInterval?. Perhaps my rich-oo-java-based mind is not the correct way to face the problems in the simplistic-straight-ahead-go-world. I'm all hears, to any suggestion.

The key Problem here is you need to convert a slice from one type to another type.

Converting Slices

The correct way to do this is to create a new slice and loop over it converting every single item. You can do this fast(er) if you create the array beforehand:

func ToIntervalsFromClockIntervals(clockIntervals []ClockInterval) HalfOpenIntervals {
    intervals := make(HalfOpenIntervals, 0, len(clockIntervals))
    for _, clockInterval := range clockIntervals {
        intervals = append(intervals, clockInterval)
    }
    return intervals
}

Composition

Apart from that composition is another way you could solve the problem that you want to write the GetEnclosingInterval function only once. I'm not saying it is better: it has other advantages and disadvantages. What fits better depends on how else you use the slices apart from what you posted here.

Here my refactored suggestion (and fixed): https://play.golang.org/p/Ko43hJUMpyT (TehSṕhinX you forgot to make the mutable method baseIntervals.add with pointer recievers instead ofvalue recievers(or whatever is the name for non-pointer recievers))

The HalfOpenIntervals does not exist any more. Instead you have two different types for CLockIntervals and PeriodIntervals and both have the sorting and GetEnclosingInterval function implemented via a common base struct.

For convenience I added a Add function and a New... function for each of them. This is where the disadvantages come in: Since CLockIntervals (and PeriodIntervals) is not a slice any more but a struct you will need convenience functions to work with the inner slice from the outside.

-- edit --

Over generalising

Coming from a oo-oriented background myself I know the drive to avoid duplicated code at all costs.

Writing go code for more than 2 years now on a full time basis I learned that this is not always the best approach in go. Duplicating code in go for different types is a common thing I do these days. Not sure if all would agree with this statement, though.

I think maybe this is overcomplicated. Define FindEnclosingHalfOpenInterval as a standalone function, and then for functions which need to work with HalfOpenInterval structs, make then accept an interface that has a EnclosingHalfOpenInterval method which usually just calls FindEnclosingHalfOpenInterval. That way you can do any kind of edge case data encapsulation without changing the signature.

And that's really all there is to it. FindEnclosingHalfOpenInterval is already shared because it's not explicitly part of any struct.