Go language Decorator Programming

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Previously wrote a python modifier functional programming, this mode is easy to put some functions into other functions, you can make your code more simple, you can also make some "small functional" code reusability is higher, so that the function in the code can be like Lego toys to assemble freely. So, for a long time, I've been decoration this programming model for decorators, writing a go language-related article here.

Read the Python decorator the article of the classmate, must know that this is a function of programming play--with a high-order function to wrap. A lot of nagging, about functional programming, you can see I wrote an article "Functional programming", this article is mainly, want to pass from the process programming thinking mode transition to functional programming thinking mode, so as to drive more people to play functional programming, so if you want to understand functional programming, Then you can read it first. Therefore, the Go language Decorator programming mode, in fact, is the mode of functional programming.

However, it is important to note that the "sugar" in the Go language is not much, and that it is also a strongly typed static non-virtual machine language, so it is impossible to code an elegant decorator like Java and Python. Of course, maybe I just caishuxueqian, if you know more writing, please tell me. I thanked him first.

Simple example

Let's look at an example first:

package mainimport "fmt"func decorator(f func(s string)) func(s string) {        return func(s string) {                fmt.Println("Started")                f(s)                fmt.Println("Done")        }}func Hello(s string) {        fmt.Println(s)}func main() {        decorator(Hello)("Hello, World!")}

This is the same as Python, except, sadly, Go does not support @decorator syntactic sugars like python. So, it's a little ugly on the call. Of course, if you want to make the code easier to read, you can: we can see that we're using a higher-order function decorator() that, when invoked, Hello() passes the function in, and then returns an anonymous function that, in addition to running its own code, invokes the incoming Hello() Function.

Let's look at one more example of the run-time calculation:

Package Mainimport (   "FMT"    "reflect"    "runtime"    "Time") type Sumfunc Func (Int64, Int64) Int64func getfunctionname (i interface{}) string {  return runtime. FUNCFORPC (reflect. ValueOf (i). Pointer ()). Name ()}func Timedsumfunc (f sumfunc) Sumfunc {  return func (start, end int64) Int64 {     Defer func (t time. Time) {      fmt. PRINTF ("---time Elapsed (%s):%v---\ n",           Getfunctionname (f), time. Since (t))     } (time. Now ())     return f (Start, end)   }}func Sum1 (start, end Int64) Int64 {  var sum Int64  sum = 0  if Start > End {    start, end = end, start  }   for I: = start; I <= end; i++ {    sum + = I  }  return sum}func Sum2 (start, end int64) Int64 {   if Start &Gt End {    start, end = end, Start  }  return (End-start + 1) * (end + start)/2} Func Main () {  sum1: = Timedsumfunc (SUM1)   sum2: = Timedsumfunc (Sum2)   fmt. Printf ("%d,%d\n", Sum1 ( -10000, 10000000), sum2 (-10000, 10000000))}


1) There are two Sum functions, the Sum1() function is simply to do a loop, the Sum2() function uses the data formula. (Note: Start and end are likely to have negative numbers) for the above code, there are a few things to illustrate:

2) The code uses the Go language reflector machine to get the function name.

3) The Modifier function istimedSumFunc()

Post-run output:

$ go run time.sum.go--- Time Elapsed (main.Sum1): 3.557469ms ------ Time Elapsed (main.Sum2): 291ns ---49999954995000, 49999954995000


Let's take a look at a related example of HTTP request processing. An example of HTTP-related

Let's look at a simple HTTP Server code.

Package Mainimport (         "FMT"           "Log"          "Net/http"           "strings") func Withserverheader (H http. Handlerfunc) http. Handlerfunc {        return func (w http. Responsewriter, R *http. Request) {                log. PRINTLN ("--->withserverheader ()")                  w.header (). Set ("Server", "HelloServer v0.0.1")                  h (W, R)         }}func Hello (w http. Responsewriter, R *http. Request) {        log. Printf ("recieved Request%s from%s\n", R.url. Path, R.remoteadDR)         fmt. fprintf (W, "Hello, world!" +r.url. Path)}func Main () {        http. Handlefunc ("/v1/hello", Withserverheader (Hello))         err: = http. Listenandserve (": 8080", nil)         if err! = Nil {                 log. Fatal ("Listenandserve:", err)         }}


Thus, we can write a number of such functions. As shown below, there are write HTTP response headers, there are write authentication cookies, there are check authentication cookies, there are logging ... In the above code, the decorated mode is used, and the Withserverheader ()   function is a Decorator, which passes in a   http. Handlerfunc , and then returns a rewritten version. The above example is relatively simple, with   Withserverheader ()   You can add a Response Header.

Package Mainimport (         "FMT"           "Log"          "Net/http"           "strings") func Withserverheader (H http. Handlerfunc) http. Handlerfunc {        return func (w http. Responsewriter, R *http. Request) {                log. PRINTLN ("--->withserverheader ()")                  w.header (). Set ("Server", "HelloServer v0.0.1")                  h (W, R)         }}func WithAuthCookie (H http. Handlerfunc) http. Handlerfunc {        return func (w http. Responsewriter, R *http. Request) { &Nbsp;              log. PRINTLN ("--->withauthcookie ()")                  cookie: = &http. Cookie{name: "Auth", Value: "Pass", Path: "/"}                 http. Setcookie (w, Cookie)                  h (W, R)         }}func Withbasicauth (H http. Handlerfunc) http. Handlerfunc {        return func (w http. Responsewriter, R *http. Request) {                log. PRINTLN ("--->withbasicauth ()")                  cookie, err: = R.cookie ("Auth")    &Nbsp;            if Err! = Nil | | Cookies. Value! = "Pass" {                         w.writeheader (http. Statusforbidden)                          return                 }                 h (W, R)         }}func WithDebugLog (H http. Handlerfunc) http. Handlerfunc {        return func (w http. Responsewriter, R *http. Request) {                log. PRINTLN ("--->withdebuglog")   &nbsP;             r.parseform ()                  log. Println (R.form)                  log. PRINTLN ("Path", R.url. Path)                 log. PRINTLN ("scheme", R.url. Scheme)                 log. Println (r.form["Url_long")                  for k, V: = Range R.form {                         log. Println ("Key:", K)                           log. Println ("Val:", Strings. Join (V, ""))                 }                 h (W, R)          }}func Hello (w http. Responsewriter, R *http. Request) {        log. Printf ("recieved Request%s from%s\n", R.url. Path, r.remoteaddr)         fmt. fprintf (W, "Hello, world!" +r.url. Path)}func Main () {        http. Handlefunc ("/v1/hello", Withserverheader (Withauthcookie (hello)))          http. Handlefunc ("/v2/hello", Withserverheader (Withbasicauth (hello)))          http. Handlefunc ("/v3/hello", Withserverheader (Withbasicauth (Withdebuglog (hello)))          ERR: = http. Listenandserve (": 8080", nil)         if err! = Nil {                 log. Fatal ("Listenandserve:", err)         }}


in use, you need to set up a layer of functions, it seems not very good looking, if you need more decorator, the code will be more ugly. Well, we can refactor. Pipeline of multiple decorators

Refactoring, we need to write a tool function--to traverse and invoke each decorator:

type HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFuncfunc Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {        for i := range decors {                d := decors[len(decors)-1-i] // iterate in reverse                h = d(h)        }        return h}

Then we can use it as follows.

http.HandleFunc("/v4/hello", Handler(hello,                WithServerHeader, WithBasicAuth, WithDebugLog))


Generic decorator is this code easier to read? The function of the pipeline is also coming out.

However, for Go decorator mode, there is a small problem-it seems to be unable to do generics, like the above calculation of the time function, its code coupled with the function needs to be modified interface type, can not be very common, if this thing can not solve, then, this decorator mode is still a bit of a bad use.

Because the go language is not a dynamic language like Python and Java,python, and Java has a language virtual machine, so they can do a bit more perverted things, but the go language is a static language, which means that its type needs to be done at compile time, otherwise it will not compile. However, Go language support of the largest generics is interface{} also a relatively simple reflection mechanism, in the above make a fuss, should still be able to fix.

Nonsense not to say, below is I use the reflection mechanism to write a more general modifier (for readability, I deleted the error-judging code)

Func Decorator (Decoptr, fn interface{}) (err error) {        var Decoratedfunc, Targetfunc reflect. Value        decoratedfunc = reflect. ValueOf (DECOPTR). Elem ()         targetfunc = reflect. ValueOf (fn)         v: = reflect. Makefunc (Targetfunc.type (),                 func (in []reflect. Value) (out []reflect. Value) {                         fmt. Println ("before")                          out = Targetfunc.call (in)                     &Nbsp;    fmt. Println ("after")                          return                 })          Decoratedfunc.set (v)         return}


The above Decorator() requires two parameters, the code above uses a reflect.MakeFunc() function to produce a new function which targetFunc.Call(in) calls the modified function. On the Go language reflection mechanism, the official article recommended-"The laws of Reflection", here I do not say much.

    • The first one is the parameter decoPtr , which is the function after finishing the modification.
    • The second one is the entry fn , which is the function that needs to be modified

Is there a second way to write this? Yes, indeed. However, this is the best code I can write in the Go language. If you know more elegant, please tell me!

OK, let's see how it works. First, let's say we have two functions that need to be modified:

func foo(a, b, c int) int {        fmt.Printf("%d, %d, %d \n", a, b, c)        return a + b + c}func bar(a, b string) string {        fmt.Printf("%s, %s \n", a, b)        return a + b}

Then, we can do this:

type MyFoo func(int, int, int) intvar myfoo MyFooDecorator(&myfoo, foo)myfoo(1, 2, 3)


Well. If you do not want to declare the function signature, then you can also find that, Decorator() when using, you also need to declare a function signature, it feels so silly. It's not a generic type, is it?

mybar := barDecorator(&mybar, bar)mybar("hello,", "world!")


Again, if you have a better spelling, please tell me. Well, it doesn't look so beautiful, but it works. It seems that the go language does not have its own features like Java or Python, so we can only ask the go language to put more sugar!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.