在Go中编写通用数据访问功能

I'm writing code that allows data access from a database. However, I find myself repeating the same code for similar types and fields. How can I write generic functions for the same?

e.g. what I want to achieve ...

type Person{FirstName string}
type Company{Industry string}

getItems(typ string, field string, val string) ([]interface{}) {
    ...
}

var persons []Person
persons = getItems("Person", "FirstName", "John")

var companies []Company
cs = getItems("Company", "Industry", "Software")

So you're definitely on the right track with the idea of returning a slice of nil interface types. However, you're going to run into problems when you try accessing specific members or calling specific methods, because you're not going to know what type you're looking for. This is where type assertions are going to come in very handy. To extend your code a bit:

getPerson(typ string, field string, val string) []Person {
    slice := getItems(typ, field, val)
    output := make([]Person, 0)
    i := 0
    for _, item := range slice {
        // Type assertion!
        thing, ok := item.(Person)
        if ok {
            output = append(output, thing)
            i++
        }
    }
    return output
}

So what that does is it performs a generic search, and then weeds out only those items which are of the correct type. Specifically, the type assertion:

thing, ok := item.(Person)

checks to see if the variable item is of type Person, and if it is, it returns the value and true, otherwise it returns nil and false (thus checking ok tells us if the assertion succeeded).

You can actually, if you want, take this a step further, and define the getItems() function in terms of another boolean function. Basically the idea would be to have getItems() run the function pass it on each element in the database and only add that element to the results if running the function on the element returns true:

getItem(critera func(interface{})bool) []interface{} {
    output := make([]interface{}, 0)
    foreach _, item := range database {
        if criteria(item) {
            output = append(output, item)
        }
    }
}

(honestly, if it were me, I'd do a hybrid of the two which accepts a criteria function but also accepts the field and value strings)

joshlf13 has a great answer. I'd expand a little on it though to maintain some additional type safety. instead of a critera function I would use a collector function.

// typed output array no interfaces
output := []string{}

// collector that populates our output array as needed
func collect(i interface{}) {
 // The only non typesafe part of the program is limited to this function
 if val, ok := i.(string); ok {
   output = append(output, val) 
 }
}

// getItem uses the collector  
func getItem(collect func(interface{})) {
    foreach _, item := range database {
        collect(item)
    }
}

getItem(collect) // perform our get and populate the output array from above.

This has the benefit of not requiring you to loop through your interface{} slice after a call to getItems and do yet another cast.