邻里街坊

这几天看了几集<情满四合院>,这帮老演员演得真不错。也就不对标那些个小鲜肉了,他们除了长得好看,绯闻多。除此之外,没啥可关注的。老演员的一个眼神,一个动作都透着一股劲。这股劲能把人带到戏里面去,能让观众情不自禁的把自己带到那种氛围里面。 好像看的并不是别人家的事情,而是自己家的家长里短。 轮台词,没有华丽的辞藻。轮布景,就是在影棚里面搭出来的景。论演员,刚才说了,没有好看的小鲜肉。 轮特效,没有!论剧情,家长里短,好不出彩。可为啥能打动人,应该是演员的"敬业"精神吧。每个演员都把自己的角色演活了,演的非常生动,非常立体。就好像这些个演员都出自身边环境一样,没有丝毫的距离和陌生感。这种好片子太少了,好演员也太少,因此敬业的精神就显得越发珍贵了。

本节来聊Golang中的结构体(Struct)。Golang的缔造者绝对对C有着很深的感情,Golang从里到外都透着C的感觉。就感觉Golang是C的豪华升级版。结构体,又名Struct。除了在C/C++之外,很少会使用到。这里暂且不谈Struct在C里面的使用方式,只聊在Golang中应该如何使用。

首先来看Struct在Golang中处于什么作用。前文说过Golang中有几种基本数据类型,背不出来,屁股要挨打的哦😔。但凡写过代码的人都清楚,只靠这几种数据类型是无法满足业务逻辑需要的,成熟的语言一定会满足用户自定义数据类型的需要。 在Golang当中,Struct就是为了满足自定义数据类型而诞生的。如果非要类比,那Struct就可以对标成Java里面的Class。比如下面:

type News struct{
    Title string
    Content string
    Date string
}

News就是一个标准的Struct,里面有三个属性。由此,可以推导出Struct的声明语法:

type name struct {
   member definition;
   member definition;
   ...
   member definition;
}

name是struct的名称,也可以理解成类名。member可以理解成成员变量,这里要注意,如果首字母大写,则属于public。如果小写,则属于private。而definition则是变量类型,这个类型可以是基本类型也可以是自定义类型,总之是合法数据类型就可以。

一旦声明完成,恭喜你,你拥有了一个独属于你的数据类型。但怎么使用呢?先来看初始化。最简单的方式:

myNews := News{
    Title:"xxx",
    Content:"xxxx",
    Date:"xxxx",
    }

这是最简单的初始化方式,其中不是每个变量都需要给值的,想给几个就给几个,想不给,那就直接 myNews := News{}。高兴就好!

初始化之后,就轮到使用了。 怎么用?最简单的方式:myNews.Title就可以了。 如果高兴,也可以仿照Java来个Setter/Getter函数。没关系,不用担心别人说三道四。黑猫白猫,最合适的就是好猫。只要结构清晰,团队成员看起来舒服,学习成本低。爱用哪个就用哪个。

到此,你就可以随心所欲,敞开了用成员变量。甭担心会出错,年轻人写代码不出错还叫年轻人嘛!反正错了在fix呗。如果说看到这里,你就认为Struct说完了,那就找个地方泡杯茶,然后再回来吧。 注意到没有,上面所有的内容都是变量,说白了,还没涉及到成员函数呢。没有函数的Struct,就是一具没有骨骼的肉体,所以下面上骨骼。

和Java不同,Golang的Struct函数不是定义在Struct内部的,而是在外部。例如:

type News struct{
    Title string
    Content string
    Date string
}

func (news *News)GetTitle(){....}

GetTitle就是News的成员函数,除了News的实例能调用。其它人无权调用。这里的写法是不是和以前讲解的函数声明方式不同?也没多什么,就多了一个"归宿"而已。我们告诉编译器,这个函数是属于News的。

在有的团队中(可能是Java转型过来的吧),喜欢声明成这样:

func (this *News)GetTitle(){ this.xxxx .... }

这样使用this会比较顺手。由此看出,news也好,this也罢。只是一个指代当前实例的指针名称而已。 叫啥都行,哪个顺手用哪个。反正只在函数内部生效。

有没有人会提问?这些都是实例方法(Java的说法),有没有静态方法?有呀!,把(this *News)拿掉,函数首字母大写,就是静态方法了。 也就是在此之前,我们一直在写的那些个代码。

Struct在Golang当中,说重要其实也不重要。因为就是一个自定义数据类型而已,无非是添加了一些成员变量,还有成员方法,只要注意首字母大小写就没问题了。 但如果说不重要吧,也有失偏颇,Golang代码中的大部分都是依靠各种各样的Struct来实现的。 因此对于Struct来说,战略上藐视,战术上重视比较合适。

因为Struct也是一种数据类型,所以以前说的各种变量使用场景,这里都适用。比如,将一个Struct当做参数传入函数中:

package main

import "fmt"

type Books struct {
   title string
   author string
   subject string
   book_id int
}
func main() {
   var Book1 Books    /* Declare Book1 of type Book */
   var Book2 Books    /* Declare Book2 of type Book */

   /* book 1 specification */
   Book1.title = "Go Programming"
   Book1.author = "Mahesh Kumar"
   Book1.subject = "Go Programming Tutorial"
   Book1.book_id = 6495407

   /* book 2 specification */
   Book2.title = "Telecom Billing"
   Book2.author = "Zara Ali"
   Book2.subject = "Telecom Billing Tutorial"
   Book2.book_id = 6495700

   /* print Book1 info */
   printBook(Book1)

   /* print Book2 info */
   printBook(Book2)
}
func printBook( book Books ) {
   fmt.Printf( "Book title : %s\n", book.title);
   fmt.Printf( "Book author : %s\n", book.author);
   fmt.Printf( "Book subject : %s\n", book.subject);
   fmt.Printf( "Book book_id : %d\n", book.book_id);
}

再比如结合分水岭中的指针,对Struct进行指针操作:

package main

import "fmt"

type Books struct {
   title string
   author string
   subject string
   book_id int
}
func main() {
   var Book1 Books   /* Declare Book1 of type Book */
   var Book2 Books   /* Declare Book2 of type Book */

   /* book 1 specification */
   Book1.title = "Go Programming"
   Book1.author = "Mahesh Kumar"
   Book1.subject = "Go Programming Tutorial"
   Book1.book_id = 6495407

   /* book 2 specification */
   Book2.title = "Telecom Billing"
   Book2.author = "Zara Ali"
   Book2.subject = "Telecom Billing Tutorial"
   Book2.book_id = 6495700

   /* print Book1 info */
   printBook(&Book1)

   /* print Book2 info */
   printBook(&Book2)
}
func printBook( book *Books ) {
   fmt.Printf( "Book title : %s\n", book.title);
   fmt.Printf( "Book author : %s\n", book.author);
   fmt.Printf( "Book subject : %s\n", book.subject);
   fmt.Printf( "Book book_id : %d\n", book.book_id);
}

如果猜的不错,你肯定是鼠标快速划过就完事了。 别走神,里面真有彩蛋。为啥提出这两个例子,是因为具体实践中,这是经常大意犯的错误。先看这两个例子的区别:

func printBook( book Books ) { ... }
func printBook( book *Books ) { ... }

一个是值传递,一个是引用传递。 因此在第一个例子中,如果printBook里面修改了数据,比如:

book.title = "lala"

外面仍然无法感知,因为是值传递。修改的是副本,不是本体。但如果在第二个例子中,修改了数据,外层就会收到影响,因为都是本体。

package main

import "fmt"

type Books struct {
    title   string
    author  string
    subject string
    book_id int
}

func main() {
    var Book1 Books /* Declare Book1 of type Book */
    var Book2 Books /* Declare Book2 of type Book */

    /* book 1 specification */
    Book1.title = "Go Programming"
    Book1.author = "Mahesh Kumar"
    Book1.subject = "Go Programming Tutorial"
    Book1.book_id = 6495407

    /* book 2 specification */
    Book2.title = "Telecom Billing"
    Book2.author = "Zara Ali"
    Book2.subject = "Telecom Billing Tutorial"
    Book2.book_id = 6495700

    /* print Book1 info */
    printBook(Book1)
    fmt.Println(Book1.title)
    /* print Book2 info */
    printBook(Book2)
}
func printBook(book Books) {
    book.title = "lala"
    fmt.Printf("Book title : %s\n", book.title);
    fmt.Printf("Book author : %s\n", book.author);
    fmt.Printf("Book subject : %s\n", book.subject);
    fmt.Printf("Book book_id : %d\n", book.book_id);
}

跑一下,看看结果就能看出区别了。

所以就这里需要注意一下,其它关于Struct的坑应该算是没有了吧。 如果有,恩,那就有吧。 坑坑何其多,岂能填的完。如果哪天我开公司,一定要招一个叫"田德湾"的员工,就冲着名字,吉利!

results matching ""

    No results matching ""