小松的技术博客

六和敬

若今生迷局深陷,射影含沙。便许你来世袖手天下,一幕繁华。 你可愿转身落座,掌间朱砂,共我温酒煮茶。

go语言的哲学

最近在看go语言,主要是我想知道如果没有了类的概念,该如何优雅的编程。

不得不说go语言很简洁,但却很灵活。初次接触它的语法,会被奇奇怪怪的外表所迷惑,不过回头来看,是go语言有着的一套风格迥异的概念设计,有一些基础的概念,然后其它的都是基础概念上的包装。随着逐步的认识深入和更多的实践,也就慢慢的能够体会go语言的精华了。

一、类型系统

go语言是强类型的语言,每一个数据都有它的类型,可以简单枚举一下:

  • 内置类型:int,float32,float64,string,bool,error,array,slice,map,channal
  • 别名机制: type Color int
  • 定义函数类型(别名): type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
  • 定义结构体: type StructName struct
  • 定义接口: type InterfaceName interface
  • 指针类型:如*int

很简单,但是通过定义结构体,我们可以定义出丰富的类型;通过定义函数类型,我们可以实现高阶函数,练习多了,也就对上面的很熟悉了。go语言仍然有指针的相关操作,对于写java类的程序员还是略微蛋疼,不过熟悉就好。

二、函数和方法

不管任何一门语言,函数和方法总是很相近的,只不过方法是属于一个具体的类或者结构体的。而go语言让它和函数走得更近了,并且方法不是定义在结构体中的。

一个函数的例子:

func SayTo(name string){
    fmt.Println("hello ",name)
}

SayTo("Json")

一个方法的例子:

type Person struct{
    name string
}

func (p Person) SayTo(other *Person){
    fmt.Println(p.name + "say hello to ",other.name);
}

//这种写法是传入的是数据结构的拷贝
// func (p Person) SayTo(other Person){
//     fmt.Println(p.name + "say hello to ",other.name);
// }

p1 := Person{name:"Jake"}
p2 := Person{name:"John"}
p1.SayTo(&p2)

会比java类语言繁琐一些,但是这样结构更加松散一些,也就更灵活一点。需要注意的是方法不过是把struct作为默认的第一个参数传进去,因此使用指针类型更合适一些:

/如果要修改到p,那么就必须传入指针类型了,函数体类用了p.name而不是*p.name是因为go语言会帮你纠正,你也可以那么写。
func (p *Person) SayTo(other *Person){
    fmt.Println(p.name + "say hello to ",other.name);
}

三、组合

go语言没有继承与多态,但是可以组合,加上它提供的一些语法糖,也可以实现类似继承的用法。

组合和继承从基础概念上就是不同的:举个例子,当提到人和学生的关系时,以类系统为基础的继承会说:人是父类,学生继承自人,拥有人的属性和行为,并有自己的特殊之处。而组合会说,人是基本,学生是人的基础上增加了特殊的行为,他包含了人的结构,故也可以调用人的属性和行为,并且可以有自己的特殊之处。感觉组合更符合现实生活。

看go语言是如何组合的:

type People struct{
    name string
    age int
}

func (p *People) Say(){
    fmt.Println("my name is",p.name,"and i am",p.age)
}

type Student struct{
    People
    language string
}

// func (p People) Say(){
//   fmt.Println("my name is",p.name,"and i am",p.age,"and i am a student")
// }

func (s Student) Study(){
    fmt.Println("i am learning", s.language)
}

//调用
p := People{name:"Jake",age:17}
s := Student{People{name:"Bob",age:16},"English"}
p.Say()
s.People.Say()
s.Say() //语法糖:如果Student没有这个方法,则它等价于s.People.Say()
s.Study()

上面可以看到语法糖,让调用更加方便,不过当Student也有这个方法的时候,就会默认调用自己的,这样就可以达到继承与函数覆写的效果。

四、接口

虽然go语言也有接口,但这个其它语言的接口完全不是一回事,它更加灵活。对于其它语言,接口或者说协议吧,是先规定好协议,再去写自己的类并且实现这些协议。这样其实是不够灵活的,如果协议更改了怎么办?或者你想要采用另外一套协议怎么办?

所以go语言采用了新的形式,它和struct完全是松散的,也许一个struct实现了某个接口而struct自己都不知道。因为接口的描述是:只要实现了接口所规定的所有方法,它就实现了这个接口。这基本上就是一些语言的鸭子类型:只要走路像鸭子,那么就可以把鸭子看。

看个直观的例子

type Student struct{
    name string
    age int
}

func (p Student) Say(){
    fmt.Println("my name is",p.name,"and i am",p.age)
}

type Person interface {
    Say()
}

我定义了个struct为Student,它实现了Say()方法,然后我又定义了个interface为Person,它规定了Say()方法。两者没有更多的联系了,但是我们却可以如下调用:

var p Person
p = Student{"John",15}
p.Say()

很灵活吧。用多了你就会喜欢这些特性了。除此,go语言给了个空接口interface{},那么任何一个数据结构都可以说是实现了这个接口,所以它就可以存储所有类型的数据了,不过也有人评论这就相当于java的Object对象了。不管怎样,实用就行。

总而言之,go语言还是值得学习的,多玩一会儿,你会喜欢上它的。

←支付宝← →微信 →
comments powered by Disqus