3 различных способа использования функции в качестве значения в Go
Go – это статически типизированный компилируемый язык. Он похож на любой другой язык со статической типизацией, и также он очень простой. Что касается функционального стиля программирования, он не страдает огромной многословностью, как Java.
Функции являются первоклассными значениями в Go. Благодаря этому факту, мы можем легко написать в нем код функционального стиля.
В этой статье я расскажу о трех способах программирования на Go.
ПО промежуточного слоя
Ниже мы создаем обработчик для работы с приложением. Функция соответствует интерфейсу http.Handler
:
func myhandler(w http.ResponseWriter, r *http.Request) {
log.Println("Executing my handler")
w.Write([]byte("OK"))
}
Мы также создаем еще несколько промежуточных обработчиков / промежуточных программ для решения сквозных проблем, таких как аутентификация и протоколирование других работ:
func authHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Executing auth handler")
// Do auth hander stuff
next.ServeHTTP(w, r)
})
}
func logHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Executing log handler")
// Do log handling stuff
next.ServeHTTP(w, r)
})
}
Наконец, в основной функции мы создаем функцию ultimateHandler
, которая будет выполняться после выполнения всех задач, таких как ведение журнала и аутентификация для каждого запроса. Для этого мы связываем несколько обработчиков в строке 5:
func main() {
mux := http.NewServeMux()
ultimateHandler := http.HandlerFunc(myhandler)
mux.Handle("/", logHandler(authHandler(ultimateHandler)))
log.Println("Listening on :3000...")
err := http.ListenAndServe(":3000", mux)
log.Fatal(err)
}
По сути, когда мы создаем переменную с помощью http.HandlerFunc
, мы создаем оболочку.
Динамическая функция
Это еще один шаблон, который я люблю использовать в своем коде всякий раз, когда могу. Это может выглядеть как Java-шаблон создания функциональных классов или потоковый API. Но мы также можем использовать этот стиль кодирования с минимальным объемом кода и меньшей сложностью:
package main
import (
"fmt"
)
type Data []int
type UnaryOperation func(op int) int
func (d Data) Apply(operation UnaryOperation) Data {
for i := 0; i < len(d); i++ {
d[i] = operation(d[i])
}
return d
}
func main() {
square := func(d1 int) int { return d1 * d1 }
qube := func(d1 int) int { return d1 * d1 * d1 }
dataset := Data{1, 2, 3}
x := dataset.Apply(qube).Apply(square)
fmt.Println(x)
}
Объяснение
- В строке 8 мы определяем тип (функцию
UnaryFunction
, которая принимает 1 аргумент) функции, которая принимаетint
и возвращает другой тип int. Определив это, мы можем создать любой другой тип, который может соответствовать его структуре, и с помощью вывода мы можем достичь динамической функции в Golang. - Строки 18 и 19 определяют две функции, которые соответствуют функции, определенной в строке 8, с другой реализацией. Строка 18 реализует метод
square
, а строка 19 реализуетcubing
.
Общий код
Мы можем использовать замыкания, чтобы написать общий код на Golang. Рассмотрим этот код:
package main
import (
"net/http"
)
type Subscriber struct {
Name, Address, LocationId, SubscriptionName string
Tenure int8
BalanceDue float32
}
type ServiceCallback func(subscriberId string, subscriber *Subscriber )
type SubscriberEntitlementService http.Client
type SubscriberDetailsService http.Client
func (se *SubscriberDetailsService) CallForSubscriberDetails() ServiceCallback{
return func(subscriberId string, sub *Subscriber){
// Call details service and fill the sub object
}
}
func (se *SubscriberEntitlementService) CallForSubscriberEntitlements() ServiceCallback{
return func(subscriberId string, sub *Subscriber){
// Call entitlements service and fill the sub object
}
}
func main() {
subscriberId:= "2389438"
subscriber := &Subscriber{}
// User proper urls and with all configurations like timeout of different sort etc.
subscriberEntitlementService := &SubscriberEntitlementService{}
subscriberDetailsService := &SubscriberDetailsService{}
entitlementsService_Callback := subscriberEntitlementService.CallForSubscriberEntitlements()
detailsService_Callback := subscriberDetailsService.CallForSubscriberDetails()
// Schedule the go routines separately
go func(){entitlementsService_Callback(subscriberId, subscriber)}()
go func(){detailsService_Callback(subscriberId, subscriber)}()
// or Use a job scheduler like rio to process them in a load balanced way
// https://github.com/susamn/rio/blob/master/examples/webapp.go
}
Объяснение:
- У нас есть две службы:
SubscriberDetailsService
иSubscribeEntitlementService
, которые возвращает типServiceCallback
. - Их
ServiceCallbacks
можно обрабатывать отдельно или с помощью балансировщика нагрузки и планировщика заданий, таких как rio .
В нашем примере обратные вызовы службы могут быть вызваны в любом месте, но когда они вызываются, они дают родительскому объекту управление механизмом вызова, и поскольку они имеют один и тот же тип ( ServiceCallback
), их можно собирать или назначать приоритеты по желанию разработчика.