2012-08-11 2 views
2

Я пишу код, который позволяет осуществлять доступ к данным из базы данных. Однако я нахожу, что повторяю тот же код для похожих типов и полей. Как я могу написать общие функции для одного и того же?Написание общих функций доступа к данным в Go

например. что я хочу достичь ...

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") 

ответ

2

Таким образом, вы определенно находитесь на правильном пути с идеей возврата куска нулевого типа интерфейса. Тем не менее, вы столкнулись с проблемами при попытке доступа к определенным членам или вызова определенных методов, потому что вы не будете знать, какой тип вы ищете. Вот где утверждают утверждения типа. Чтобы продлить ваш код немного:

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 
} 

Так что, что делает это он выполняет общий поиск, а затем отсеивает только те элементы, которые имеют правильный тип. В частности, утверждение типа:

thing, ok := item.(Person) 

проверяет, является ли переменная item имеет тип Person, и если да, то она возвращает значение и верно, в противном случае она возвращает ноль и ложь (таким образом, проверка хорошо говорит нам, если утверждение преуспело).

Вы можете, если хотите, сделать еще один шаг и определить функцию getItems() с точки зрения другой булевой функции. В принципе идея будет иметь getItems() запустить функцию передать его по каждому элементу в базе данных, и только добавить, что элемент результатов при работе функции на элемент возвращает истину:

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

(честно говоря, если бы это было меня, я бы сделал гибрид из двух, который принимает функцию критериев, но также принимает строки полей и значений)

+0

На самом деле я бы использовал функцию коллектора, которая делает для меня добавление. Функция может закрываться над срезом [] и выполнять добавление добавления к этому фрагменту, если утверждение типа соответствует. Это будет работать более универсально, чем getItem, который возвращает результаты и будет более безопасным, поскольку «выход» может быть типизированным срезом. –

+0

Да, но это не значит, что вы не можете предсказать тип вывода, поэтому он должен быть интерфейсом nil (или его срезом)? Возможно, я не понимаю, какие изменения вы предлагаете ...? – joshlf

+0

См. Мой ответ для подробного объяснения :-). –

2

joshlf13 имеет отличный ответ. Я бы немного расширил его, чтобы сохранить дополнительную безопасность. вместо функции критерия я бы использовал функцию коллектора.

// 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. 

Это имеет преимущество, не требуя от вас петлю через интерфейс кусочком {} после вызова GetItems и сделать еще один бросок.

+0

Функция коллектора должна быть знакома, если вы когда-либо работали на языке с функцией сокращения для повторяющихся значений. –

Смежные вопросы