首页 > 编程语言 > 详细

go语言圣经第六章(读书笔记)

时间:2019-07-24 19:38:33      阅读:98      评论:0      收藏:0      [点我收藏+]

第六章 方法

  • “尽管没有被大众所接受的明确的OOP的定义,从我们的理解来讲,一个对象其实也就是一个
    简单的值或者一个变量,在这个对象中会包含一些方法,而一个方法则是一个一个和特殊类
    型关联的函数。一个面向对象的程序会用方法来表达其属性和对应的操作,这样使用这个对
    象的用户就不需要直接去操作对象,而是借助方法来做这些事情。”

方法声明

  • 在函数声明时,在其名字之前放上一个变量,就是这个变量(或其类型)的方法。
type Point struct{ X, Y float64 }
// traditional function
func Distance(p, q Point) float64 {
    return math.Hypot(q.X-p.X, q.Y-p.Y)
}
// same thing, but as a method of the Point type
func (p Point) Distance(q Point) float64 {
    return math.Hypot(q.X-p.X, q.Y-p.Y)
}

p := Point{1, 2}
q := Point{4, 6}
fmt.Println(Distance(p, q)) // "5", function call
fmt.Println(p.Distance(q))
// "5", method call
  • 这个附加的变量(上例中的p),称为方法的接收器(receiver)
  • 这个附加的变量,相当于其它oop定义的this或者self
  • 像p.Distance的表达式叫做选择器(点操作符,还可以访问struct类型里的字段)
  • 方法可以被声明到任何类型,只要不是一个指针或者一个interface

基于指针对象的方法

  • 当变量本身比较大时,通常采用其对应指针作为接收器
func (p *Point) ScaleBy(factor float64) {
    p.X *= factor
    p.Y *= factor
}
  • “在现实的程序里,一般会约定如果Point这个类有一个指针作为接收器的方法,那么所有Point的方法都必须有一个指针接收器,即使是那些并不需要这个指针接收器的函数”
  • 译者的标注:
    1.不管你的method的receiver是指针类型还是非指针类型,都是可以通过指针/非指针类型进行调用的,编译器会帮你做类型转换
    2.在声明一个method的receiver该是指针还是非指针类型时,你需要考虑两方面的内部,第一方面是这个对象本身是不是特别大,如果声明为非指针变量时,调用会产生一次拷贝;第二方面是如果你用指针类型作为receiver,那么你一定要注意,这种指针类型指向的始终是一块内存地址,就算你对其进行了拷贝。熟悉C或者C艹的人这里应该很快能明白。
  • 补充一点:使用指针类型作为接收器,如果该方法修改了接收器变量的值,那是真的修改了;使用非指针类型作为接收器,该方法使用的只是其拷贝,修改了值对原来的变量并没有影响

Nil也是一个合法的接收器类型

// An IntList is a linked list of integers.
// A nil *IntList represents the empty list.
type IntList struct {
    Value int
    Tail
    *IntList
}
// Sum returns the sum of the list elements.
func (list *IntList) Sum() int {
    if list == nil {
        return 0
    }
    return list.Value + list.Tail.Sum()
}

通过嵌入结构体来扩展类型

  • 当嵌入类型为非指针类型
package main

import (
  "fmt"
)

type Point struct{ X, Y float64 }

func (p *Point) ins() {
  p.X++
  p.Y++
}


type ColoredPoint struct {
  Point
  Color string
}

func main() {
    cp := &ColoredPoint{
      Point: Point{X: 5, Y:6},
      Color: "red",
    }

  cp.ins()
  fmt.Println(cp)

}
  • 当嵌入类型是指针类型
package main

import (
  "fmt"
)

type Point struct{ X, Y float64 }

func (p *Point) ins() {
  p.X++
  p.Y++
}


type ColoredPoint struct {
  *Point
  Color string
}

func main() {
  cp := &ColoredPoint{
    &Point{X: 5, Y:6},
    "red",
  }

  cp.ins()
  fmt.Println(cp)
  fmt.Println(cp.Point)
  fmt.Println(cp.X)
  fmt.Println(cp.Y)
  fmt.Println(cp.Color)

}

方法值

  • 用函数值和方法值作类比会很容易理解
    1.通过选择器p.Distance获取方法值,存入变量中
    2.通过方法值变量调用该方法(就像换了个名字一样)
p := Point{1, 2}
q := Point{4, 6}
distanceFromP := p.Distance // method value
fmt.Println(distanceFromP(q)) // "5"
var origin Point // {0, 0}
fmt.Println(distanceFromP(origin)) // "2.23606797749979", sqrt(5)
scaleP := p.ScaleBy // method value
scaleP(2) // p becomes (2, 4)
scaleP(3) // then (6, 12)
scaleP(10) // then (60, 120)
  • 通过方法值的形式省去匿名函数的编写
type Rocket struct { /* ... */ }
func (r *Rocket) Launch() { /* ... */ }
r := new(Rocket)
time.AfterFunc(10 * time.Second, func() { r.Launch() })
//上下等价
time.AfterFunc(10 * time.Second, r.Launch)

封装

  • 通过大小写实现
  • 封装的优点:
    1.首先,因为调用方不能直接修改对象的变量值,其只需要关注少量的语句并且只要弄懂少量变量的可能的值即可。
    2.隐藏实现的细节,可以防止调用方依赖那些可能变化的具体实现,这样使设计包的程序员在不破坏对外的api情况下能得到更大的自由。
    3.阻止了外部调用方对对象内部的值任意地进行修

go语言圣经第六章(读书笔记)

原文:https://www.cnblogs.com/laiyuanjing/p/11240216.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!