Golang 复合数据类型:方法

sunlingbot · · 684 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

传统的面向对象编程

在面向对象编程(OOP)中,类与对象是面向对象编程的两个主要方面。一个类(Class)能够创建一种新的类型(Type),其中对象(Object)就是类的实例(Instance)。可以这样来类比:你可以拥有类型 int 的变量,也就是说存储整数的变量是 int 类的实例(对象)。

对象可以使用属于它的普通变量来存储数据。这种从属于对象或类的变量叫作字段(Field)。对象还可以使用属于类的函数来实现某些功能,这种函数叫作类的方法(Method)。这两个术语很重要,它有助于我们区分函数与变量,哪些是独立的,哪些又是属于类或对象的。总之,字段与方法通称类的属性(Attribute)

方法

在 Golang 中,方法是作用在接收者(receiver)上的一个函数,接收者是某种类型的变量;接收者可以是任意类型(指针、接口除外),包括结构体类型、函数类型、可以是 int ,boll,string或数组别名类型。

  • 接收者不能是一个接口类型,因为接口是一个抽象定义,方法却必须要具体实现
  • 接收者不能是一个指针类型,但可以是任何其他类型允许的指针

方法重载

类型的代码和绑定在它上面的方法的代码可以不放置在一起,它们可以存在不同的源文件中,唯一的要求是它们必须是同一个包的。 因为方法是函数,所以同样的,不允许方法重载,即对于一个类型只能有一个给定名称的方法,但是如果基于接收器类型,是有重载的。如下面的例子,具有同样名字的方法Add可以在 2 个或多个不同的接收器类型*denseMatrix*sparseMatrix上存在,比如在同一个包里这么做是允许的:

//a是接收器变量,*denseMatrix和*sparseMatrix是接收器类型,Add是方法,b Matrix是参数列表,Matrix是返回参数
func (a *denseMatrix) Add(b Matrix) Matrix
func (a *sparseMatrix) Add(b Matrix) Matrix

接收器的标准格式

  • recv是接收器名称,receiver_type是接收器类型( 接收器中的参数变量名在命名时,官方建议使用接收器类型名的第一个小写字母 ,例如 Socket 类型的接收器变量应该命名为 s )
  • methodName是方法名,paramater_list是参数列表,return_value_list是返回值列表
func (recv receiver_type)methodName(paramater_list)(return_value_list) {...}

接收器的类型

在计算机中,小对象由于值复制时的速度较快,所以适合使用非指针接收器,大对象因为复制性能较低,适合使用指针接收器,在接收器和参数间传递时不进行复制,只是传递指针。

  • 指针类型

    由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的 this 或者 self;但 Go 并没有 this 和 self 这两个关键字,因此,也可以使用 this 和 self 作为接收者的实例化名字(this 和 self 跟一般的实例化名字没什么两样)。

    //定义TwoInts结构体
    type TwoInts struct {
        a int
        b int
    }
    func main(){
        two1 := new(TwoInts)//实例化,返回指针*TwoInts
        two1.a = 12
        two1.b = 10
        //调用AddThem方法
        fmt.Println(two1.AddThem())
        //调用AddToParam方法
        fmt.Println(two1.AddToParam(20))
    
        two2 := TwoInts{3,4} //实例化,返回结构体TwoInts{3,4}
        //调用AddThem方法
        fmt.Println(two2.AddThem())
    }
    //指针接收器的两个值相加方法
    func (tn *TwoInts)AddThem()int{
        return tn.a + tn.b
    }
    //指针接收器的三个值相加方法
    func (tn *TwoInts)AddToParam(param int)int{
        return tn.a + tn.b + param
    }
    
    /*
    22
    42
    7
    */

    代码说明:

    • 定义一个TwoInts结构,拥有两个整型的成员变量。使用名字two1实例化TwoInts结构体,返回指针类型*TwoInts,分别设置实例化成员变量two1.atwo1,b的值为1210;使用名字two2实例化TwoInts结构体,设置成员变量返回结构体TwoInts
    • 构造成员变量的方法AddThem,设置方法的接收器类型为指针*TwoInts,返回tn.a + tn.b 的整型值;因此可以修改成员值,即便退出方法,也有效;构造另一个成员变量的方法AddToParam,设置方法的接收器类型为指针*TwoInts,返回tn.a + tn.b + param的整型值。
    • main函数中,使用fmt.Println函数分别调用AddThemAddToParam方法,获取成员变量的值。
  • 非指针类型

    当方法作用于非指针接收器时,Go语言会在代码运行时将接收器的值复制一份,在非指针接收器的方法中可以获取接收器的成员值,但修改后无效

    // 定义点结构
    type Point struct {
        X int
        Y int
    }
    // 非指针接收器的加方法
    func (p Point) Add(other Point) Point {
        // 成员值与参数相加后返回新的结构
        return Point{p.X + other.X, p.Y + other.Y}
    }
    func main() {
        // 初始化点
        p1 := Point{3, 4}
        p2 := Point{2, 5}
        // 与另外一个点相加
        result := p1.Add(p2)
        // 输出结果
        fmt.Println(result.X,result.Y,result)
    }
    
    /*
    5 9 {5 9}
    */

    代码说明:

    • 定义一个Point点结构,拥有XY两个整型的成员变量
    • Point结构定义一个非指针接收器的Add方法,传入和返回都是Point的结构,可以方便地实现多个点连续相加的效果,例如P4 := P1.Add( P2 ).Add( P3 )
    • p1p2实例化两个点
    • p1p2两个点相加后返回结果并存储到result
    • 打印结果resultXY相加的值

      由于例子中使用了非指针接收器,Add() 方法变得类似于只读的方法,Add() 方法内部不会对成员进行任何修改

### 函数和方法的区别

函数将变量作为参数:Function(recv);方法在变量上被调用:recv.Method1()

  • 当接收者是指针时,方法可以改变接收者的值和状态。(对于方法来说)
  • 当参数作为指针传递时,即通过引用调用时,函数也可以改变参数的状态。(对于函数来说)

### Golang设计模式之工厂方法

参考链接:Golang设计模式之工厂方法(掘金)

在面向对象编程中,可以通过构造子方法实现工厂模式,但 Golang 并不是面向对象编程语言,因此不能使用构造子方法来实现设计模式,而是相应地提供了其他方案。以结构体为例,通常会为结构体类型定义一个工厂,工厂的名字以new或者New开头。

//不强制构造函数,首字母大写
type File struct {
    fd int
    name string
}

//构造工厂方法
func NewFile(fd int,name string) *File{
    if fd < 0 {
        return nil
    }
    return &File{fd,name}
}

func main() {
    //调用工厂方法NewFile
    f := NewFile(10,"./test.yxy")
    fmt.Println(f)
    //计算结构体占用多少内存
    size := unsafe.Sizeof(File{})
    fmt.Println(size)
}

/*
&{10 ./test.yxy}
24
*/

代码说明:

  • 以首字母为大写,不强制使用构造函数,创建结构体File
  • 为这个结构体构造一个工厂方法NewFile,并返回指向结构体的指针*File
  • main函数调用该工厂方法

强制使用工厂方法,通过应用可见性就可以禁止使用new函数,强制用户使用工厂方法,从而使类型转变成私有的。

### 指针或值作为接收者

如果想要方法改变接收者的数据,就在接收者的指针类型上定义该方法。否则,就在普通的值类型上定义方法。

change()接收一个指向B的指针,并改变它的内部成员;write()通过拷贝接收B的值并只输出B的内容,

  type B struct {
      thing int
  }
  
  func (b *B) change() {
      b.thing = 1
  }
  
  func (b B) write() string { 
      return fmt.Sprint(b) 
  }
  
  func main() {
      var b1 B // b1是值
      b1.change()
      fmt.Println(b1.write())
  
      b2 := new(B) // b2是指针
      b2.change()
      fmt.Println(b2.write())
  }
  
  /* 
  {1}
  {1}
  */

有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:sunlingbot

查看原文:Golang 复合数据类型:方法

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

684 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传