百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Go 函数装饰器模式教程

toyiye 2024-06-21 11:59 10 浏览 0 评论

装饰器在 Python 和 TypeScript 等其他编程语言中肯定更为突出,但这并不是说你不能在 Go 中使用它们。事实上,对于某些问题,使用装饰器是我们希望在本教程中找到的完美解决方案。

了解装饰器模式

装饰器本质上允许您包装现有功能并在顶部附加或添加您自己的自定义功能。

在 Go 中,函数被视为第一类对象,这本质上意味着您可以像传递变量一样传递它们。让我们用一个非常简单的例子来看看这个:

package main

import (
  "fmt"
  "time"
)

func myFunc() {
  fmt.Println("Hello World")
  time.Sleep(1 * time.Second)
}

func main() {
  fmt.Printf("Type: %T\n", myFunc)
}

所以,在这个例子中,我们定义了一个名为 的函数myFunc,它简单地打印出Hello World。然而,在我们的main()函数体中,我们已经调用fmt.Printf并且我们习惯于%T打印出我们作为第二个参数传入的值的类型。在这种情况下,我们正在传递myFunc,随后将打印出以下内容:

$ go run test.go
Type: func()

那么,这对我们 Go 开发人员意味着什么?好吧,它强调了函数可以在我们代码库的其他部分中传递并用作参数的事实。

让我们通过进一步扩展我们的代码库并添加一个coolFunc()将函数作为其唯一参数的函数来看看这一点:

package main

import (
  "fmt"
  "time"
)

func myFunc() {
  fmt.Println("Hello World")
  time.Sleep(1 * time.Second)
}

// coolFunc takes in a function
// as a parameter
func coolFunc(a func()) {
    // it then immediately calls that functino
  a()
}

func main() {
  fmt.Printf("Type: %T\n", myFunc)
  // here we call our coolFunc function
  // passing in myFunc
    coolFunc(myFunc)
}

当我们尝试运行它时,我们应该看到我们的新输出具有我们 Hello World期望的字符串:

$ go run test.go
Type: func()
Hello World

现在,这可能会让您感到有些奇怪。你为什么想做这样的事情?它本质上为您的调用增加了一层抽象,myFunc并使代码复杂化,而没有真正增加太多价值。

一个简单的装饰器

让我们看看如何使用这种模式为我们的代码库添加一些价值。如果需要,我们可以在执行特定函数时添加一些额外的日志记录,以突出显示它的开始和结束时间。

package main

import (
    "fmt"
    "time"
)

func myFunc() {
  fmt.Println("Hello World")
    time.Sleep(1 * time.Second)
}

func coolFunc(a func()) {
    fmt.Printf("Starting function execution: %s\n", time.Now())
    a()
    fmt.Printf("End of function execution: %s\n", time.Now())
}

func main() {
    fmt.Printf("Type: %T\n", myFunc)
    coolFunc(myFunc)
}

调用此命令后,您应该会看到如下所示的日志:

$ go run test.go
Type: func()
Starting function execution: 2018-10-21 11:11:25.011873 +0100 BST m=+0.000443306
Hello World
End of function execution: 2018-10-21 11:11:26.015176 +0100 BST m=+1.003743698

如您所见,我们已经能够有效地包装我的原始函数,而无需更改它的实现。我们现在能够清楚地看到该函数何时启动以及何时完成执行,并且它向我们强调了该函数只需大约一秒钟即可完成执行。

现实世界的例子

让我们再看几个例子,看看我们如何使用装饰器来获得更多的名声和财富。我们将使用一个非常简单的 http Web 服务器并装饰我们的端点,以便我们可以验证传入请求是否具有特定的标头集。

如果您想了解更多关于在 Go 中编写简单 REST API 的信息,那么我建议您在此处查看我的另一篇文章: 在 Go 中创建 REST API

package main

import (
    "fmt"
    "log"
    "net/http"
)

func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Endpoint Hit: homePage")
    fmt.Fprintf(w, "Welcome to the HomePage!")
}

func handleRequests() {
    http.HandleFunc("/", homePage)
    log.Fatal(http.ListenAndServe(":8081", nil))
}

func main() {
    handleRequests()
}

如您所见,我们的代码中没有什么特别复杂的。我们设置了一个net/http 路由器,服务于单个/端点。

让我们添加一个非常简单的身份验证装饰器函数,它将检查请求Authorized头是否设置为true传入请求。

package main

import (
    "fmt"
    "log"
    "net/http"
)

func isAuthorized(endpoint func(http.ResponseWriter, *http.Request)) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        fmt.Println("Checking to see if Authorized header set...")

        if val, ok := r.Header["Authorized"]; ok {
            fmt.Println(val)
            if val[0] == "true" {
                fmt.Println("Header is set! We can serve content!")
                endpoint(w, r)
            }
        } else {
            fmt.Println("Not Authorized!!")
            fmt.Fprintf(w, "Not Authorized!!")
        }
    })
}

func homePage(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Endpoint Hit: homePage")
    fmt.Fprintf(w, "Welcome to the HomePage!")
}

func handleRequests() {

    http.Handle("/", isAuthorized(homePage))
    log.Fatal(http.ListenAndServe(":8081", nil))
}

func main() {
    handleRequests()
}

注意:这绝对不是保护 REST API 的正确方法,我建议您考虑使用 JWT 或 OAuth2 来实现该目标!

所以,让我们分解一下并尝试了解发生了什么!

我们创建了一个名为的新装饰器函数isAuthorized(),它接收一个与原始homePage函数具有相同签名的函数。然后返回一个http.Handler.

在我们的isAuthorized()函数体中,我们返回一个 new http.HandlerFunc来验证我们的Authorized header的工作是 set 和 equals true。现在,这是一个大大简化的版本OAuth2身份验证/授权,有一些细微的差异,但它让您大致了解它是如何工作的。

然而,要注意关键是我们已经设法装饰现有端点并在所述端点周围添加某种形式的身份验证,而无需更改该功能的现有实现。

现在,如果我们要添加一个我们想要保护的新端点,我们可以轻松地这样做:

// define our newEndpoint function. Notice how, yet again,
// we don't do any authentication based stuff in the body
// of this function
func newEndpoint(w http.ResponseWriter, r *http.Request) {
    fmt.Println("My New Endpoint")
    fmt.Fprintf(w, "My second endpoint")
}

func handleRequests() {

    http.Handle("/", isAuthorized(homePage))
  // register our /new endpoint and decorate our
  // function with our isAuthorized Decorator
  http.Handle("/new", isAuthorized(newEndpoint))
    log.Fatal(http.ListenAndServe(":8081", nil))
}

这突出了装饰器模式的主要优点,在我们的代码库中包装代码非常简单。我们可以使用相同的方法轻松添加新的经过身份验证的端点

结论

希望本教程有助于揭开装饰器的神秘面纱,以及如何在自己的基于 Go 的程序中使用装饰器模式。我们了解了装饰器模式的好处以及如何使用它来用新功能包装现有功能。

在本教程的第二部分,我们查看了一个更现实的示例,说明如何在自己的生产级 Go 系统中使用它。

如果您喜欢本教程,请随时广泛分享这篇文章,它确实对网站有帮助,我将不胜感激!如果您有任何问题和/或意见,请在下面的评论部分告诉我!


相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码