From a59111fc76990d6c20b342e97f1e8e7036b651d3 Mon Sep 17 00:00:00 2001 From: xiemengjun Date: Thu, 30 Aug 2012 23:45:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=86=99=E4=BA=86=E9=83=A8=E5=88=86=E9=9D=A2?= =?UTF-8?q?=E5=90=91=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 2.5.md | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/2.5.md b/2.5.md index 3a19107d..3c7dadc6 100644 --- a/2.5.md +++ b/2.5.md @@ -1,4 +1,185 @@ #2.5面向对象 +前面两章我们介绍了函数和struct,那你是否想过函数当作struct的字段一样来处理呢?今天我们就讲解一下函数的另一种形态,带有接收者的函数,我们称为`method` + +##method +现在假设有这么一个场景,你定义了一个struct叫做长方形,你现在想要计算他的面积,那么按照我们一般的思路应该会用下面的方式来实现 + + package main + import "fmt" + + type Rectangle struct { + width, height float64 + } + + func area(r Rectangle) float64 { + return r.width*r.height + } + + func main() { + r1 := Rectangle{12, 2} + r2 := Rectangle{9, 4} + fmt.Println("Area of r1 is: ", area(r1)) + fmt.Println("Area of r2 is: ", area(r2)) + } + +我们这个代码是可以计算出来长方形的面积,但是他不是作为长方形的一个方法(类似面向对象里面的方法)实现的,而是长方形作为函数的一个参数传入,然后计算获取的。 + +这样实现有什么问题,当然没有问题咯,但是当你代码里面增加一个圆形、正方形、多边形等的时候,然后你又想计算他们的面积的时候怎么办啊?那就增加新的函数咯,但是函数名你就必须要跟着换了,你就要把函数名变成`area_rectangle, area_circle, area_triangle...` + +这样的实现不是最优雅的,而且从概念上来说一个形状的面积应该是属于的一个特性,应该是属于这个形状的,就像他的字段一样。 + +基于上面的原有所以才有了`method`的概念,`method`是附属在一个给定的类型上的,他的语法和函数的申明语法一模一样除了在`func`后面跟一个接收者(也就是给定的类型) + +用Rob Pike的话来说就是: + +>“A method is a function with an implicit first argument, called a receiver.“ + +method的语法如下: + + func (ReceiverType r) func_name (parameters) (results) + +下面我们用最开始的例子用method来实现: + + package main + import ( + "fmt" + "math" + ) + + type Rectangle struct { + width, height float64 + } + + type Circle struct { + radius float64 + } + + func (r Rectangle) area() float64 { + return r.width*r.height + } + + func (c Circle) area() float64 { + return c.radius * c.radius * math.Pi + } + + + func main() { + r1 := Rectangle{12, 2} + r2 := Rectangle{9, 4} + c1 := Circle{10} + c2 := Circle{25} + + fmt.Println("Area of r1 is: ", r1.area()) + fmt.Println("Area of r2 is: ", r2.area()) + fmt.Println("Area of c1 is: ", c1.area()) + fmt.Println("Area of c2 is: ", c2.area()) + } + +在使用method的过程重要注意几点 + +- 虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样 +- method里面可以访问接收者的字段 +- 调用method通过`.`访问,就像struct里面访问字段一样 + +那是不是method只能作用在struct上面呢?当然不是咯,他可以定义在任何你自定义的类型、内置类型、struct等各种类型上面。这里你是不是有点迷糊了,什么自定义类似,自定义类型不就是struct嘛,不是这样的哦,struct只是自定义类型里面一种比较特殊的类型而已,还有其他自定义类型申明,可以通过如下这样的申明来实现。 + + type type_name type_literal + +请看下面这个申明自定义类型的代码 + + type ages int + + type money float32 + + type months map[string]int + + m := months { + "January":31, + "February":28, + ... + "December":31, + } + +看到了吗?简单的很吧,这样你就可以在你自己的代码里面定义有意义的类型了,设置只是一个简单的别名,例如上面ages替代了int + +好了,让我们回到`method` + +你可以在任何的自定义类型中定义任意多的`method`,接下来让我们看一个复杂一点的例子 + + package main + import "fmt" + + const( + WHITE = iota + BLACK + BLUE + RED + YELLOW + ) + + type Color byte + + type Box struct { + width, height, depth float64 + color Color + } + + type BoxList []Box //a slice of boxes + + func (b Box) Volume() float64 { + return b.width * b.height * b.depth + } + + func (b *Box) SetColor(c Color) { + b.color = c + } + + func (bl BoxList) BiggestsColor() Color { + v := 0.00 + k := Color(WHITE) + for _, b := range bl { + if b.Volume() > v { + v = b.Volume() + k = b.color + } + } + return k + } + + func (bl BoxList) PaintItBlack() { + for i, _ := range bl { + bl[i].SetColor(BLACK) + } + } + + func (c Color) String() string { + strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"} + return strings[c] + } + + func main() { + boxes := BoxList { + Box{4, 4, 4, RED}, + Box{10, 10, 1, YELLOW}, + Box{1, 1, 20, BLACK}, + Box{10, 10, 1, BLUE}, + Box{10, 30, 1, WHITE}, + Box{20, 20, 20, YELLOW}, + } + + fmt.Printf("We have %d boxes in our set\n", len(boxes)) + fmt.Println("The volume of the first one is", boxes[0].Volume(), "cm³") + fmt.Println("The color of the last one is",boxes[len(boxes)-1].color.String()) + fmt.Println("The biggest one is", boxes.BiggestsColor().String()) + + fmt.Println("Let's paint them all black") + boxes.PaintItBlack() + fmt.Println("The color of the second one is", boxes[1].color.String()) + + fmt.Println("Obviously, now, the biggest one is", boxes.BiggestsColor().String()) + } + + ## links * [目录]()