Golang中十分nice的一个func技巧

核心:函数是一等公民

简单的打印日志场景

type User struct {
	Name   string
	Age    int
}

func main() {
	user := &User{Name: "Jack", Age: 18}
	log.Printf("debug level. user:%v\n",user)
}
复制代码

简单单单,把用户信息打印一遍,问题来了,假设不同场景,需要不一样的日志格式,比如A,B,C场景需要json,难道在每一个场景都先json.Marshal(),然后再打印?如下

func main() {
	user := &User{Name: "Jack", Age: 18}
	s, _ := json.Marshal(user)
	log.Printf("user:%v\n",string(s))
}
复制代码

有一种面向对象思维的方法,User实现一个JsonString()的方法即可。但今天换一种思路,用func

一等公民func来搞点事

先写一个函数,如下

func Debug(user *User) {
	s, _ := json.Marshal(user)
	log.Printf("debug level. user:%v\n",string(s))
}
复制代码

接下来的做法不是直接调用Debug方法,而是再定义一个函数,一个形参列表可以容纳Debug方法的函数,如下

type User struct {
	Name   string
	Age    int
}

func LogUserData(user *User, fun func(u *User)) {
	fun(user)
}
func main() {
	user := &User{Name: "Jack", Age: 18}
	LogUserData(user,DebugJson)
}

func DebugJson(user *User) {
	s, _ := json.Marshal(user)
	log.Printf("debug level. user:%v\n",string(s))
}
复制代码

这样做的一个好处是定义了日志打印的框架,把具体的日志的打印细节交由外部来实现,从而保证了对修改扩展的能力。其次,若是所有日志打印需要加某些共同的前置或者后置动作,可以在LogUserData里面加,从而避免了直接调用DebugJson方法,导致的在各个场景额外加前置后置条件的重复工作。此外,若是不同的动作实现细节,具有不一样的前置或者后置动作的话,可以在动作的具体实现函数中进行增加。比如说我需要增加一个Error等级的日志打印方法,而且Error的日志需要短信来通知我系统发生error了,直接扩展就行了

func LogUserData(user *User, fun func(u *User)) {
    // 共同前置
    log.Printf("开始打印日志")
	fun(user)
	// 共同后置
	log.Printf("日志打印完成")
}
func Error(user *User) {
	// 特殊前置
	// 发送短信 do Something...
	log.Printf("error level. user:%v\n",user)
}
复制代码

其次,fun的形参列表不一定要与user保持一致,只要有一定相关就行了,比如重新定义一个判断未成年的函数

func IsAdult(user *User, fun func(age int) bool )bool {
	return fun(user.Age)
}
func main() {
	user := &User{Name: "Jack", Age: 18}
	b := IsAdult(user, JudgeAdult)
	fmt.Println(b)
}
func JudgeAdult(age int) bool {
	if age >= 18 {
		return true
	}
	return false
}
复制代码

总结

刚刚上面的做法就是 func(主体obj,动作func),又可以理解为 func(消息obj, 消息消费动作func),我把它叫做消费函数,这种做法在golang中得到了广泛的采用,比如net/http

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}
复制代码

主要的做法是用一个定好某个过程的框架的函数func,接收主体,然后接收对主体操作的action,用这个action来对主体进行某种处理。 用图片表示就是这样

比如golang net/http的HandleFunc,它只定义了收发http请求的框架,具体怎么对请求操作,回应什么内容,都交由外部来定义。

其实这就是切面的思想,在一个过程,把主体和主体的处理过程切开来,处理过程交由外部实现,从而让这个过程具有了更高的灵活度和扩展性。

附上所有代码

package main

import (
	"encoding/json"
	"log"
	"fmt"
)

type User struct {
	Name string
	Age  int
}
// 主体+动作
func LogUserData(user *User, fun func(u *User)) {
	// 共同前置
	log.Printf("开始打印日志")
	fun(user)
	// 共同后置
	log.Printf("日志打印完成")
}
// 主体+动作
func IsAdult(user *User, fun func(age int) bool )bool {
	return fun(user.Age)
}
func main() {
	user := &User{Name: "Jack", Age: 18}
	LogUserData(user,DebugJson)
	LogUserData(user,Info)
	LogUserData(user,Error)
	b := IsAdult(user, JudgeAdult)
	fmt.Println(b)
}
func JudgeAdult(age int) bool {
	if age >= 18 {
		return true
	}
	return false
}
func DebugJson(user *User) {
	s, _ := json.Marshal(user)
	log.Printf("debug level. user:%v\n", string(s))
}
func Info(user *User) {
	log.Printf("info level. user:%v\n", user)
}

func Error(user *User) {
	// 发送短信 do Something...
	log.Printf("error level. user:%v\n", user)
}


复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值