您的位置:首页 > 编程语言 > Go语言

Go圣经-学习笔记之函数和错误处理

2017-10-24 00:00 525 查看
摘要: 函数声明,递归分析网页元素,error,服务端错误处理比较好的方式

上一篇 Go圣经-学习笔记之复合类型(三)

下一篇 Go圣经-学习笔记之函数值(二)

函数声明

下面给出函数的四种声明方法:

func add(x int, y int) int { return x+y }

func add(x, y int) (z int) { z = x + y ; return z }

func first(x int, _ int) int { return x }
func second(int, int) int { return 0 }

存在必有其存在的价值,那后两者存在的应用场景在哪里呢?我表示持续懵逼中......, 我写了这个场景,看可以不?

type Interface interface {
Add(int, int) int
Sub(int, int) int
Mul(int, int) int
}

type Idem struct{}

func (i Idem) Add(x int, y int) int {
return x + y
}

func (i Idem) Sub(int, int) int {
return 0
}

func (i Idem) Mul(x int, _ int) int {
return x * 2
}

Go语言有一个非常核心的特性:组合,上一篇文章也提及了。如果一个对象是由多个接口、匿名方法集或者对象的自身行为集构成。如果这个对象觉得有些行为是它忽略或者不想要的,这时候就可以采用上述的后两者方法去声明方法,但是这个是方法的声明,不是函数的声明。能想到的就这么多,其他我就不知道了。

函数递归

对于递归的运用,我们通过一个url,获取网页数据流,然后通过html标准库解析数据,获取各个标签元素节点。

说明: 运用html标准库解析完数据流,得到的是一个多叉树的元素节点,包括:文档节点、文本节点、元素标签节点、评论节点、注脚节点和错误节点。由这六个节点构成一颗多叉树


DEMO实例代码片段

**********************************************************************
// html标准库相关说明:
type Node struct {
Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node

Type      NodeType
DataAtom  atom.Atom
Data      string
Namespace string
Attr      []Attribute
}
func Parse(r io.Reader) (*Node, error)
**********************************************************************

var (
purl = flag.String("url", "https://www.golang.org", "website url")
)

func main() {
var (
resp *http.Response
err  error
node *html.Node
)
flag.Parse()
if resp, err = http.Get(*purl); err != nil {
log.Fatal(err.Error())
return
}
node, err = html.Parse(resp.Body)
if err != nil {
log.Fatal(err.Error())
return
}
resp.Body.Close()
// 我们采用多叉树的深度遍历算法,并打印所有节点元素url。
for _, link := range visit(nil, node) {
fmt.Println(link)
}
}

func visit(links []string, node *html.Node) []string {
if node.Type == html.ElementNode && node.Data == "a" {
for _, attr := range node.Attr {
links = append(links, attr.Val)
}
}
for child := node.FirstChild; child != nil; child = child.NextSibling {
links = visit(links, child)
}
return links
}

递归元素的存储是由stack数据结构存储的。C++等主流语言的stack栈是由固定大小的,如果过多递归会导致栈内存溢出。但是Go语言的栈是动态分配的,按需分配,一般不需要考虑stack溢出安全问题,但是还是要多多尽量节约内存

大家吐槽比较多的函数返回error

在Go语言标准库使用过程中,几乎每个函数或者方法返回参数的最后一个类型都是error,类似:

func xx(sss type, ...) (xxx type, ..., err error)

比如,我们经常这样做:

***************************************************
// ioutil标准库:
func ReadAll(r io.Reader) (data []byte, err error)
***************************************************

if bts, err := ioutil.ReadAll(r); err !=nil{
err = errors.Wrap(err, "xxxxx")
return
}

大家觉得如果调用的每个方法或者函数,都要处理error,大家比较崩溃。Go语言官方是这样解释返回error的设计的:

由于对于某个应该在控制流程中处理的错误而言,将这个错误以异常的形式抛出会混乱对错误的描述,这通常会导致一些糟糕的后果。当某个程序错误被当作异常处理后,这个错误会将堆栈信息返回给用户,这些信息复杂且无用,无法帮助定位错误

所以Go语言想通过控制流if,return语句处理异常,这使得编程人员能更多的关注错误处理。

在这里给大家提供用于error追踪的包,很好用。github地址:errors

// 我一般这样使用
// 业务逻辑层
func AddSaleOrder(so *SaleOrder, o *orm.Ormer) (retCode int, err error) {
if _, err = (*o).Insert(so); err != nil {
err = errors.Wrap(err, "AddSaleOrder")
retCode = consts.DB__INSERT_ERROR
return
}
return
}

// 控制层
func (s *SaleOrderController) AddSaleOrder() {
var so *models.SaleOrder
......
if retCode, err = models.AddSaleOrder(so, nil); err !=nil {
Logger.Error(err.Error()) // 这里的err.Error() 会返回整个的error跟踪调用链
s.Data["json"] = map[string]interface{}{
"err_code": retCode,
"err_msg": errors.Cause(err).Error(), // 这个返回err调用链中产生错误最初始的形态。
}
s.ServeJSON()
return
}
s.Data["json"] = map[string]interface{}{
"err_code": 0,
"err_msg": "",
}
s.ServeJSON()
return
}

这样做的好处是:我们服务端既可以记录错误产生的整个调用链跟踪,同时,前端也返回了关键错误。屏蔽了不想要用户看到的信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: