面向对象编程

  • Golang支持OOP特性
  • Golang中没有class类,但是struct类似class, Golang通过struct来实现OOP特性
  • Golang去掉了传统OOP语言的继承,方法重载,构造函数,析构函数,隐藏的this指针
  • 继承,封装和多态,与其他语言实现方式不一样
  • 通过接口interface关联,耦合度很低,非常灵活

结构体 struct

  • 变量 = 对象,实例

  • 把对象的特征提取出来 -> 字段、属性 和 行为 -> 创建变量(实例)

  • 自定义的数据类型

    type 结构体名 struct {
        //字段 (属性信息)
    }
  • 不同结构体的变量字段互相独立,互不影响。

  • 结构体是值类型,传递时进行值拷贝

  • 创建结构体

    • 方式1
      - 方式2
      ``` var person Person = Person {}
    • 方式3
      var person *Person = new (Person) // 结构体指针
      //指针赋值标准写法
      (*person).Name = "xiaoming"
      // 也可以这么写,GO语言编译器底层实现,更符合编程习惯,所以做了简化
      person.Name = "xiaoming"
    • 方式4
      var person *Person = Person{} // 结构体指针
  • struct 结构体内存分配

    • 变量总是预先加载到内存中进行运算
    • 所有字段在内存中是连续的
    • 两个结构体如果需要转换,需要完全相同
    • 结构体进行type重新定义,会生成新的数据类型,互相之间可以强制转化
    • struct 每个字段,可以写上一个tag,该tag可以通过反射机制获取。常见的场景是序列化和反序列化
  • 方法

    • 作用在指定数据类型上的,因此自定义类型都可以有方法
    • 方法的声明和调用
      type Person struct{
          Name string
      }
      
      func(p Person) test() { // test和Person类型绑定,只能通过Person类型调用
          fmt.Println("Name = ", p.Name)
      }
    • 通过一个变量调用方法时,其调用机制和函数一样
    • 不同的是,变量调用时,变量本身也会当做一个参数传递到方法当中
    • 为了提高效率,通常方法和结构体的指针类型进行绑定
  • Golang的结构体没有构造函数(get.set),可以使用工厂模式来解决这个问题

    • 如果一个包中结构体是小写字母开头的(私有),则无法在其他包中直接调用
      package model
      type student Student{
          Name string,
          score float64 
      }
      // 工厂模式
      func NewStudent(n string, s float64) *student{
          return &student{
              Name: n
              Score: s
          }
      }
        
      func (s *student) GetScore() float64{
          return s.score
      }
      
      package main
      func main(){
          var stu = model.NewStudent("tom",88)
          var s = stu.GetScore()
      }

面向对象编程思想 - 抽象

  • 三大特征
    • 继承
    • 封装
    • 多态
  • 封装

    • 字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包通过被授权的操作,才能对字段进行操作
    • 隐藏实现的细节
    • 可以对数据进行验证,保证安全合理
    • 对结构体的属性进行封装
    • 通过方法,包实现封装
    • 实现步骤
      • 讲结构体,字段的首字母小写
      • 给结构体提供一个工厂模式的函数,首字母大写,类似构造函数
      • 提供一个首字母大写的Set方法,一共来对属性判断并赋值
      • 提供首字母大写的Get方法,用于获取属性的值
  • 继承

    • 为什么需要继承:防止代码冗余,复用代码,利于维护
    • 多个结构体有相同属性或者方法时,可以抽象出一个更高层的结构体
    • 其他的结构体不需要重新定义属性和方法,只需要嵌套一个匿名struct
      tpye Goods struct{
          Name string
          Price int
      }
      
      type Books struct{
          Goods
          Writers string
      }
    • 结构体可以使用嵌套匿名结构体所有字段和方法,包括小写字母开头的属性和方法
    • 匿名结构体的访问可以简化
    • 逐层查找匿名结构体中的字段,就近原则
    • 如果匿名结构体中有相同的字段时(结构体本身没有该字段),调用时必须指定匿名结构体的名字,否则编译器会报错
    • 如果struct嵌套了一个有名字的结构体,则表示组合关系。调用时,必须带结构体名
    • 多重继承
      • 一个结构体嵌套了多个匿名结构体,那么就是多重继承

接口 Interface

  • Golang编程中大量存在

    type USB interface {
    
    }
  • 只要是实现了接口,实现了接口变量所声明的所有方法

  • 松耦合,高内聚的编程思想&多态

  • interface类型可以定义一组方法,但是不需要实现,并且interface不能包含任何变量。到某个具体的自定义类型时,在实现具体功能。

    type 接口名 interface{
        method1 (参数列表) 返回列表
        method2 (参数列表) 返回列表
    }
  • 只要一个变量实现了接口类型的所有方法,那么这个变量就实现了这个接口. (golang中没有implement这样的关键字)

  • 一个自定义类型实现了某个接口,才能将该自定义类型的实例赋给该接口

  • 只要是自定义数据类型,都可以实现接口,不只是结构体

  • 一个自定义类型可以实现多个接口

  • golang的接口中不允许有变量

  • 一个接口A可以继承多个接口B,C, 此时如果需要实现A接口,则需要实现B,C接口的所有方法

  • 接口是一个引用类型

  • 空接口 interface{},所有类型都实现了空接口,所以可以把任何类型赋给空接口

  • 当A结构体继承了B结构体,那么A结构体就自动继承了B结构体的字段和方法,并且可以直接使用

  • 当A结构体需要功能扩展,同时又不希望破坏继承关系,则就可以去实现某个接口即可,因此我们可以认为,实现接口是对继承机制的补充

继承和接口的关系

  • 解决的问题不同

    • 继承的价值在于:解决代码的复用性和可维护性
    • 接口的价值在于:设计,设计好各种规范,让其他自定义类型去实现这些方法
  • 接口更加灵活

    • 继承 is-a
    • 接口 like-a
  • 接口在一定程度上实现了代码的解耦

多态

  • 变量具有多种形态,通过接口来实现。

类型断言

  • 由于接口是一般类型,不知道具体是什么类型,如果要转成具体类型,就需要类型断言
  • 如果类型不匹配,就会报panic
  • 最佳实践在实例有特有方法时,对接口进行类型断言,使其能够调用实例的特有方法
  • 写一个循环,判断传入的参数是什么类型,需要使用到空接口

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Project Design 上一篇
Golang Learning 下一篇