• -------------------------------------------------------------
  • ====================================

17-HyperLedger-Fabric基础-开发ChainCode的Go语言基础

区块链 dewbay 4年前 (2020-01-06) 2043次浏览 已收录 0个评论 扫描二维码

这一个星期,主要是在敲代码。照着例子敲,随着一遍遍的敲,对之前不太懂的 go 语言也渐渐清楚了一些。在这里,将开发ChainCode过程中用到的 GO 语言总结一下。

在 Fabric 中, 开发一个智能合约,简单来讲,就是定义一个结构体,然后给这个结构体实现两个接口函数 Init( ) 和 Invoke( )。

所以,接下来会根据ChainCode开发经验,逐一介绍此过程中出现的Go 语言基础知识。全文将按照如下流程展开:17-HyperLedger-Fabric基础-开发ChainCode的Go语言基础

全文结构

一、Go 语言中的结构体

Go 语言的结构体(struct)和其他语言的类(class)有同等的地位,但 Go 语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性。组合是复合类型的基础。在 Go 语言中, 所有的类型(指针类型除外)都可以有自己的方法。所以,Go 语言的结构体也可以定义自己的方法。

1、Go 语言结构体的定义

例如,我们要定义一个矩形类型:

type Rect struct {
    x, y float64
    width, height float64
}

2、Go 语言结构体成员函数的定义

然后我们定义成员方法 Area()来计算矩形的面积:

func (r *Rect) Area() float64 {
    return r.width * r.height
}

在 Go 语言中,func 为定义函数的关键字。(r *Rect)表示该函数为结构体 Rect 的成员函数。该函数的返回值类型为 float64,写在函数定义行的最后。

3、Go 语言结构体的初始化

在定义了 Rect 类型后,该如何创建并初始化 Rect 类型的实例呢?这可以通过如下几种方法实现:

rect1 := new{Rect}
rect2 := &Rect{}
retc3 := &Rect{0,0,100,200}
retc4 := &Rect{width: 100, height: 200}

在 Go 语言中,未进行显式初始化的变量都会被初始化为该类型的零值,例如 bool 类型的零值为 false,int 类型的零值为 0,string 类型的零值为空字符串.

在 Go 语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以 NewXXX 来命名,表示“构造函数”:

func NewRect(x, y, width, height float64) *Rect {
    return &Rect{x, y, width, height}
}

这一切非常自然,开发者也不需要分析在使用了 new 之后到底背后发生了多少事情。在 Go 语言中,一切要发生的事情都直接可以看到。


二、Go 语言的接口

1、侵入式接口

Go 语言的接口并不是其他语言(C++、Java、C#等)中所提供的接口概念。在 Go 语言出现之前,接口主要作为不同组件之间的契约存在。对契约的实现是强制的,你必须声明你的确实现了该接口。为了实现一个接口,你需要从该接口继承。如在 java 语言中:

interface IFoo {
    void Bar();
}

class Foo implements IFoo { // Java 文法
// ...
}

要实现 IFoo 接口,必须要在 Foo 类的实现过程中声明实现该接口。

这类接口我们称为侵入式接口。
“侵入式”的主要表现在于实现类需要明确声明自己实现了某个接口

2、非侵入式接口

在 Go 语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口,

type File struct {
// ...
}
func (f *File) Read(buf []byte) (n int, err error)
func (f *File) Write(buf []byte) (n int, err error)
func (f *File) Seek(off int64, whence int) (pos int64, err error)
func (f *File) Close() error

这里我们定义了一个 File 类,并实现有 Read()、Write()、Seek()、Close()等方法。设

想我们有如下接口:

type IFile interface {
    Read(buf []byte) (n int, err error)
    Write(buf []byte) (n int, err error)
    Seek(off int64, whence int) (pos int64, err error)
    Close() error
}

type IReader interface {
    Read(buf []byte) (n int, err error)
}

type IWriter interface {
    Write(buf []byte) (n int, err error)
}

type ICloser interface {
    Close() error
}

尽管 File 类并没有从这些接口继承,甚至可以不知道这些接口的存在,但是 File 类实现了

这些接口,可以进行赋值:

var file1 IFile = new(File)
var file2 IReader = new(File)
var file3 IWriter = new(File)
var file4 ICloser = new(File)

因此,在 Go 语言的非侵入式接口中,我们实现某一接口,并不需要在进行声明。


三、Go 的 strconv 包的使用

在 go 语言中,用到了很多的字符串处理相关的包,在ChainCode的编写过程中,与字符串相关的类型转换都是通过 strconv 包实现的。这里主要介绍该包的两个字符转换函数。

1、strconv.Atoi

该函数的主要功能是将字符串类型转换为数字类型。

该函数原型如下:

strconv.Atoi(s string) (i int, err error) 将字符串转换为 int 型。

利用多返回值的特性,该函数会返回 2 个值,第 1 个是转换后的结果(如果转换成功),第 2 个是可能出现的错误,因此,我们一般使用以下形式来进行从字符串到其它类型的转换

val, err = strconv.Atoi(s)

2、strconv.Itoa

该函数的主要功能是将数字类型转换到字符串类型。

该函数原型如下:

strconv.Itoa(i int) string 返回数字 i 所表示的字符串类型的十进制数。

该函数仅仅返回一个十进制数对应的字符串类型。


四、Go 中 json 包的使用

在目前ChainCode的开发过程中,选择的数据库是 CouchDB。

CouchDB 是一种文档型数据库,CouchDB 支持原生的 JSON 和字节数组的操作,基于 JSON 的操作,可以支持复杂的查询。如果存储的数据是字节数组,也支持基本的键值对操作。所以在这个过程中,不可避免的要在结构体对象和 JSON 数据之间进行相互转换。这里主要介绍两个函数。

1、json.Marshal | 将一个对象转换为 json 格式的数据

  • 函数原型如下:

func Marshal (v interface{ }) ([]byte, error)

在 go 语言中,要把数据转换为 json 格式,可以使用 encoding/json 包中的 Marshal 函数进行数据的转换,Marshal 函数可以接收任意类型的参数,转换完成后,返回字节码数组和错误信息。

  • 使用案例

创建一个结构

type Message struct {
    Name string
    Body string
    Time int64
}

使用 Marshal 函数对数据进行转换

m := Message{"Alice", "Hello", 1294706395881547000}
b, err := json.Marshal(m)

关于 Struct tag 的使用。
在将对象转换成 Json 数据的过程中,有一个关于 Struct tag 的知识点。可以使用它来改变对象所对应的 json 数据的字段。
比如,对于如下结构体
type Message struct {
Name string
Body string
Time int64
}
假如我想让 Name 换成小写 name,或者其他的字段,
例如 username,我就可以加上 struct tag:(以 username 为例
type Message struct {
Name string `json:”username”`
Body string
Time int64
}
这样子得到的 json 数据如下:
{
username: “Alice”,
Body: “Hello”,
Time: 1294706395881547000,
}

2、json.Unmarshal | 将 json 数据解析成一个对象

要解析 json 数据,可以使用 encoding/json 包中的 Unmarshal 函数进行解析

  • 该函数的原型如下:
func Unmarshal(data []byte, v interface{}) error

该函数输入一个 json 字符串,将其解析成对象 v。转换完成之后,返回错误信息。

  • 使用案例

上接 json.marshal 的使用案例。首先,需要创建一个结构,用来存储转换后的数据

var m Message

解析 json 数据

err := json.Unmarshal(b, &m)

五、Go 语言中的 nil

在编写 go 语言程序的时候,经常会写如下判断

if err != nil{    
    // do something  
}  

当出现不等于 nil 的时候,说明出现某些错误了,需要我们对这个错误进行一些处理,
而如果等于 nil 说明运行正常。

那什么是 nil 呢?

nil 的意思是无,或者是零值。
在 Go 语言中,如果你声明了一个变量但是没有对它进行赋值操作,那么这个变量就会有一个类型的默认零值。对于未进行显式初始化的变量都会被初始化为该类型的零。

例如 bool 类型的零值为 false,int 类型的零值为 0,string 类型的零值为空字符串.

Go 的文档中说到,nil 是预定义的标识符,代表指针、通道、函数、接口、映射或切片的零值,也就是预定义好的一个变量。


参考资料:

go 语言编程-json 数据处理

《Go 语言编程》-第三章-面向对象编程-许式伟、吕桂华

 

 

转自知乎 苏小乐 :https://www.zhihu.com/people/shan-de-ding-zhu/activities


露水湾 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:17-HyperLedger-Fabric基础-开发ChainCode的Go语言基础
喜欢 (0)
[]
分享 (0)
关于作者:
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址