Files
build-web-application-with-…/2.5.md
2012-08-31 11:47:30 +08:00

5.5 KiB
Raw Blame History

#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())
}

LastModified

  • Id