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), их можно собирать или назначать приоритеты по желанию разработчика.

Ответить