How does programming mode Go implement decorators
preface
Hello, everyone, I’m Asong. Today I want to talk to you about how to implement decorator code with Go. Why do I have this idea? Recently, a large amount of decorator code was used in this project because the project needed to look at Python code all the time. One decorator code can be shared throughout the project, reducing redundant code. Python syntax sugar make it easy to realize a decorator, but the language of sugar is not much, but it is strongly typed static language without a virtual machine, so there is no way to do it like Java and python write elegant decoration of code, but also can implement, today we’ll look at how to write a decorator Go language code!
What is a decorator
Before introducing the basic concept of decorators, let’s take an example that is appropriate for decorators:
Now our living standard has improved, basic everyone a mobile phone, we also know that the mobile phone screen fell to the floor is very easy to break the screen, a broken mobile phone screen, and to spend a sum of money for maintenance, is very heartache; So what can be done to avoid this problem and make our phones more resistant without damaging the structure of their screens? In fact, we only need to spend a few yuan to buy a toughened film, which makes the phone more resistant to falling without changing the structure of the original phone screen.
This example leads us to the core of this article -> decorators. The essence of a decorator is:
Function decorators are used to “mark” functions in source code to enhance their behavior in some way.
Decorators are a powerful feature, but to master them you must understand closures! The concept of closures is explained in the next section. Let’s first look at how Python uses decorators:
def metric(fn):
@functools.wraps(fn)
def timer(*arag, **kw):
start = time.time()
num = fn(*arag, **kw)
end = time.time()
times = (end - start) * 1000
print('%s executed in %s ms' % (fn.__name__, times))
return num
return timer
@metric
def Sum(x, y):
time.sleep(0.0012)
return x + y;
Sum(10.20)
Copy the code
Metric is a decorator function that can be applied to any function and print the execution time of that function. Having this decorator makes it much easier to know the execution time of any function.
A brief summary of decorator usage scenarios:
- Insert log: Makes section-oriented programming easier.
- Caching: Read and write caching is implemented using decorators to reduce redundant code.
- Transaction handling: Makes code look cleaner.
- Permission verification: Permission verifier is a set of code, reduce redundant code.
There are many more decorator usage scenarios, but let’s take a look at how to use Go to implement decorator code.
closure
Decorator implementations are inseparable from closures, so let’s learn what closures are!
Closures are often confused with anonymous functions because it is not common to define functions inside functions until you start using anonymous functions. Also, closures are only a problem when nested functions are involved. Therefore, many people know both concepts at the same time.
In essence, a closure is a scoped function that contains non-global variables referenced in the function body but not defined in the body. It doesn’t matter if a function is anonymous, the key is that it can access non-global variables defined outside the definition body.
Closures are hard to understand conceptually, but let’s do it by example.
func makeAverager(a) func(val float32) float32{
series := make([]float32.0)
return func(val float32) float32 {
series = append(series, val)
total := float32(0)
for _,v:=range series{
total +=v
}
return total/ float32(len(series))
}
}
func main(a) {
avg := makeAverager()
fmt.Println(avg(10))
fmt.Println(avg(30))}Copy the code
In this case, what do you think the result is? 10,30, 10,20?
Run it and the answer comes out: 10,20. Why is that? Let’s analyze it!
Writing the makeAverager in the above code is not allowed in C, because in C, the allocation of memory within a function is on the stack. When the makeAverager returns, that part of the stack is reclaimed, but in Go there is no problem. Go build –gcflags=-m./test/test1. Go
# command-line-arguments
test/test1.go:21:13: inlining call to fmt.Println
test/test1.go:22:13: inlining call to fmt.Println
test/test1.go:8:2: moved to heap: series
test/test1.go:8:16: make([]float32.0) escapes to heap
test/test1.go:9:9: func literal escapes to heap
test/test1.go:21:17: avg(10) escapes to heap
test/test1.go:21:13: []interface {} literal does not escape
test/test1.go:22:17: avg(30) escapes to heap
test/test1.go:22:13: []interface {} literal does not escape
<autogenerated>:1: .this does not escape
Copy the code
From the results of the run, we can see that series, FUNc, and AVG all escaped to the heap. If func(val float32) float32{} is referenced by series, float32{} will not be destroyed.
To summarize: A closure is a function that preserves the binding of the free variables that existed when the function was defined, so that when a function is called, those bindings can still be used even though the definition scope is no longer available.
Note that only functions nested within other functions may need to handle external variables that are not in global scope.
Application of decorators in Gin
You should have used Gin this Web framework, the use of its registered in routing provides middleware, can intercept the HTTP request – response life cycle of a particular function, in the life cycle of request – response can register multiple middleware, each middleware perform different functions, an intermediate again after the next turn middleware execution. This middleware is actually used as a decorator. Let’s look at a simple example:
func VerifyHeader(a) gin.HandlerFunc {
return func(c *gin.Context) {
header := c.Request.Header.Get("token")
if header == "" {
c.JSON(200, gin.H{
"code": 1000."msg": "Not logged in",})return}}}func main(a) {
r := gin.Default()
group := r.Group("/api/asong",VerifyHeader())
{
group.GET("/ping".func(context *gin.Context) {
context.JSON(200,gin.H{
"message": "pong",
})
})
}
r.Run()
}
Copy the code
This code is very simple, we just need to write a VerifyHeader function, when registering the route to add it, when there is a request, the gin.HanderFunc function will be executed first, in the gin framework to use a slice to store, so when adding middleware, pay attention to add order oh!
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
finalSize := len(group.Handlers) + len(handlers)
if finalSize >= int(abortIndex) {
panic("too many handlers")
}
mergedHandlers := make(HandlersChain, finalSize)
copy(mergedHandlers, group.Handlers)
copy(mergedHandlers[len(group.Handlers):], handlers)
return mergedHandlers
}
Copy the code
Net/HTTP uses decorators
The application of decorators to the Gin framework, as we saw above, greatly reduces the amount of redundant code and makes the code more extensible. So let’s practice by implementing a decorator on the standard library HTTP package.
We know that the HTTP standard library for the Go language cannot use middleware, so here’s our chance to give it one! Look at the code:
type DecoratorHandler func(http.HandlerFunc) http.HandlerFunc
func MiddlewareHandlerFunc(hp http.HandlerFunc, decors ... DecoratorHandler) http.HandlerFunc {
for d := range decors {
dp := decors[len(decors)- 1-d]
hp = dp(hp)
}
return hp
}
func VerifyHeader(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("token")
if token == "" {
fmt.Fprintf(w,r.URL.Path +" response: Not Logged in")
return
}
h(w,r)
}
}
func Pong(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,r.URL.Path +"response: pong")
return
}
func main(a) {
http.HandleFunc("/api/asong/ping",MiddlewareHandlerFunc(Pong,VerifyHeader))
err := http.ListenAndServe(": 8080".nil)
iferr ! =nil {
log.Fatal("ListenAndServe: ", err)
}
}
Copy the code
It’s still fairly simple to implement. Here we redeclare the DecoratorHandler type, essentially func(http.handlerfunc) http.handlerfunc, to make it easier to add middleware functions, which are executed in the order they were added.
conclusion
Ok, this article is over here, this article, we study the concept of closure, through closure we learned how to use decorators in the language, because the language does not support annotations in the syntactic sugar, so use a decorator or a bit ugly, but the mind is still quite important, we can refer to this kind of thought in daily development, Write better code!
Quality three even (share, praise, look) are the author continue to create more quality content motivation! I am aasong
And we’ll see you next time.
We have created a Golang learning and communication group. Welcome to join the group and we will learn and communicate together. Way to join the group: pay attention to the public account. For more learning materials, please go to the official number.
Welcome to the public account: Golang Dream Factory
Recommended previous articles:
- Learning channel design: From getting started to giving up
- How does the Go language implement reentrant locking?
- Which of the Go languages do you use to allocate memory, new or make?
- Source analysis panic and recover, do not understand you hit me!
- The scene of large face blows caused by empty structures
- Interviewer: Can you talk about the conversion between string and []byte?
- Interviewer: What is the result of two nil comparisons?
- Interviewer: Can you use Go to write some code to determine how the current system is stored?
- If you write binary search in an interview