函數式編程


本文主要分享【函數式編程】,技術文章【Go語言設計模式之函數式選項模式】為【THMAIL】投稿,如果你遇到Go,設計模式相關問題,本文相關知識或能到你。

函數式編程

背景:

在看OpenTelemetry-gin源碼時遇到一個函數
func Middleware(service string, opts ...Option) gin.HandlerFunc
可以看到這個函數是以Option為參數的;
點進去發現Option是一個接口,

type Option interface {
   
	apply(*config)
}

Middleware內是這樣使用該參數的:

func Middleware(service string, opts ...Option) gin.HandlerFunc {
   
	cfg := config{
   }
	for _, opt := range opts {
   
		opt.apply(&cfg)
	}

綜上不難發現,如果我們要成功調用Middleware函數,就要實現一個Option類,該類主要用applay方法來進行參數的裝配;

調研

調研發現這是go的一種設計模式:選項模式

1.一般的選項模式都是函數選項模式,例子如下:

我們先定義一個OptionFunc的函數類型

type OptionFunc func(*Option)

然后利用閉包為每個字段編寫一個設置值的With函數:

func WithA(a string) OptionFunc {
   
	return func(o *Option) {
   
		o.A = a
	}
}

func WithB(b string) OptionFunc {
   
	return func(o *Option) {
   
		o.B = b
	}
}

func WithC(c int) OptionFunc {
   
	return func(o *Option) {
   
		o.C = c
	}
}

然后,我們定義一個默認的Option如下:

var (
	defaultOption = &Option{
   
		A: "A",
		B: "B",
		C: 100,
	}
)

最后編寫我們新版的構造函數如下:

func newOption2(opts ...OptionFunc) (opt *Option) {
   
	opt = defaultOption
	for _, o := range opts {
   
		o(opt)
	}
	return
}

測試一下:

func main() {
   
	x := newOption("nazha", "小王子", 10)
	fmt.Println(x)
	x = newOption2()
	fmt.Println(x)
	x = newOption2(
		WithA("沙河娜扎"),
		WithC(250),
	)
	fmt.Println(x)
}

輸出:

&{
   nazha 小王子 10}
&{
   A B 100}
&{
   沙河娜扎 B 250}
2.openTelemetry-gin中用到的也是函數選項模式(也可稱為結構體選項模式)

接口

type Option interface {
   
	apply(*config)
}

調用

func Middleware(service string, opts ...Option) gin.HandlerFunc {
   
	cfg := config{
   }
	for _, opt := range opts {
   
		opt.apply(&cfg)
	}
	if cfg.TracerProvider == nil {
   
		cfg.TracerProvider = otel.GetTracerProvider()
	}
	tracer := cfg.TracerProvider.Tracer(
		tracerName,
		oteltrace.WithInstrumentationVersion(SemVersion()),
	)
	if cfg.Propagators == nil {
   
		cfg.Propagators = otel.GetTextMapPropagator()
	}
	return func(c *gin.Context) {
   
		c.Set(tracerKey, tracer)
		savedCtx := c.Request.Context()
		defer func() {
   
			c.Request = c.Request.WithContext(savedCtx)
		}()
		ctx := cfg.Propagators.Extract(savedCtx, propagation.HeaderCarrier(c.Request.Header))
		opts := []oteltrace.SpanStartOption{
   
			oteltrace.WithAttributes(semconv.NetAttributesFromHTTPRequest("tcp", c.Request)...),
			oteltrace.WithAttributes(semconv.EndUserAttributesFromHTTPRequest(c.Request)...),
			oteltrace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(service, c.FullPath(), c.Request)...),
			oteltrace.WithSpanKind(oteltrace.SpanKindServer),
		}
		spanName := c.FullPath()
		if spanName == "" {
   
			spanName = fmt.Sprintf("HTTP %s route not found", c.Request.Method)
		}
		ctx, span := tracer.Start(ctx, spanName, opts...)
		defer span.End()

		// pass the span through the request context
		c.Request = c.Request.WithContext(ctx)

		// serve the request to the next middleware
		c.Next()

		status := c.Writer.Status()
		attrs := semconv.HTTPAttributesFromHTTPStatusCode(status)
		spanStatus, spanMessage := semconv.SpanStatusFromHTTPStatusCodeAndSpanKind(status, oteltrace.SpanKindServer)
		span.SetAttributes(attrs...)
		span.SetStatus(spanStatus, spanMessage)
		if len(c.Errors) > 0 {
   
			span.SetAttributes(attribute.String("gin.errors", c.Errors.String()))
		}
	}
}

可以看到這種方法本質上還是進行方法的調用,不同的是可以將一組屬性封裝到一個apply中,不用一個屬性寫一個函數裝配;

參考:

https://www.liwenzhou.com/posts/Go/functional_options_pattern/

本文《Go語言設計模式之函數式選項模式》版權歸THMAIL所有,引用Go語言設計模式之函數式選項模式需遵循CC 4.0 BY-SA版權協議。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: