Go一些tips

装饰器, 函数式编程

普通的装饰器

package main_test

import (
	"fmt"
	"testing"
)

func decorator(f func(s string)) func(s string){
	return func(s string){
		fmt.Printf("started")
		f(s)
		fmt.Printf("ended")
	}
}


func Hello(s string){
	fmt.Print("hello")
}

func TestDecorator(t *testing.T){
	//decorator(Hello)("s")
	hello := decorator(Hello)
	hello("s")
}

时间装饰器

package main_test

import (
	"fmt"
	"reflect"
	"runtime"
	"testing"
	"time"
)

type SumFunc func(int64, int64) int64

// 使用了 Go 语言的反射机器来获取函数名
func getFuncName(i interface{}) string{
	return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

func timeSumFunc(f SumFunc) SumFunc{
	return func(start, end int64) int64{
		defer func(t time.Time){
			fmt.Printf("Time (%s) spend %v", getFuncName(f), time.Since(t))
		}(time.Now())
		return f(start, end)
	}
}

func Sum1(start, end int64) int64 {
	var sum int64 = 0
	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 > end {
		start, end = end, start
	}
	return (end - start + 1) * (end + start) / 2
}


func TestTimeDecorator(t *testing.T){
	sum1 := timeSumFunc(Sum1)
	sum2 := timeSumFunc(Sum2)

	t.Log(sum1(-100, 10000))
	t.Log(sum2(-1000, 100000))
}

多个修饰器的 Pipeline

package main_test

import (
	"log"
	"net/http"
	"strings"
	"testing"
)

type HttpHandlerDecorator func(handlerFunc http.HandlerFunc) http.HandlerFunc

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


func hello(w http.ResponseWriter, r *http.Request) {
	log.Printf("Recieved Request %s from %s\n", r.URL.Path, r.RemoteAddr)
}
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) {
		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")
		if err != nil || cookie.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")
		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 TestPipeLine(t *testing.T){
	http.HandleFunc("v4/hello", Handler(hello,  WithServerHeader, WithBasicAuth, WithDebugLog))
}


转自酷 壳 – COOLSHELL

Channel转发函数

package main_test

import "testing"

// 会把一个整型数组放到一个Channel中,并返回这个Channel
func echo(nums []int) <-chan int {
	out := make(chan int)
	go func() {
		for _, n := range nums {
			out <- n
		}
		close(out)
	}()
	return out
}
// 平方
func sq(in <- chan int) <- chan int{
	out := make(chan int)
	go func(){
		for n := range in {
			out <- n * n
		}
		close(out)
	}()
	return out
}

// 过滤奇数函数
func odd(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		for n := range in {
			if n%2 != 0 {
				out <- n
			}
		}
		close(out)
	}()
	return out
}

// 求和函数
func sum(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		var sum = 0
		for n := range in {
			sum += n
		}
		out <- sum
		close(out)
	}()
	return out
}


func TestChannel(t *testing.T){
	var nums = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	for n := range sum(odd(sq(echo(nums)))){
		t.Log(n)
	}
}


type EchoFunc func([]int) (<-chan int)
type PipeFunc func(<- chan int)(<-chan int)

func pipeline(nums []int, echo EchoFunc, pipelines ...PipeFunc) <- chan int {
	ch := echo(nums)
	for i:= range pipelines{
		ch = pipelines[i](ch)
	}
	return ch
}

func TestChannel2(t *testing.T){
	var nums = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	for n := range pipeline(nums, echo, sq, odd, sum){
		t.Log(n)
	}
}


Buy me a 肥仔水!