【golang】面向对象编程OOP

194 阅读3分钟

前言

本人之前是写Java的,众所周知Java是典型的面向对象编程语言,那go怎么实现面向对象编程呢?
本文就是来探索go如何进行面向对象编程!

面向对象三大特性

  • 封装
  • 继承
  • 多态

组合

组合是go语言实现面向对象编程的关键。

实现封装

go语言可以通过定义结构体来封装对象的属性和行为,比如:

type Person struct {
   Name string
   Age  int
}

func (person Person) eat() {
    // ...
}
func (person Person) drink() {
    // ...
}
func (person Person) sleep() {
    // ...
}
func (person Person) work() {
    // ...
}

func main() {
   person := Person{"张三", 18}
   person.eat()
   person.drink()
   person.sleep()   
   person.work()
}

实现继承

对象继承

go语言可以通过结构体嵌入实现对象的继承。

简单介绍下结构体嵌入和匿名成员(详见):
什么是匿名成员
结构体的成员仅指定成员的数据类型而不指定成员的名称,这类成员就是匿名成员。匿名成员也有成员名称,就是类型名称,可以通过类型名称访问匿名成员。
什么是结构体嵌入?
结构体嵌入机制让一个结构体包含另一个结构体类型的匿名成员,这样就可以通过简单的点运算符x.f来访问匿名成员链中嵌套的x.d.e.f成员。总之,结构体嵌入机制方便访问嵌套结构体的成员和方法

示例:

type Point struct {
   X, Y int
}

func (p Point) getPoint() (int, int) {
   return p.X, p.Y
}

type Circle struct {
   Point
   Radius float64
}

func (c Circle) getArea() float64 {
   return math.Pi * c.Radius * c.Radius
}

func main() {
   circle := Circle{Point{1, 2}, 3}
   // 直接访问匿名结构体成员的属性
   fmt.Println(circle.X, circle.Y)
   // 直接访问匿名结构体成员的方法
   x, y := circle.getPoint()
   fmt.Println(x, y)
   fmt.Println(circle.getArea())
}

可以看到,通过把Point结构体嵌入Circle结构体中,Circle对象看起来也拥有了Point对象的属性和方法。

接口继承

go语言可以通过接口内嵌实现接口的继承。
示例:

type I1 interface {
   Method1() string
}

// I2接口嵌入了I1接口,则I2继承了I1接口
type I2 interface {
   I1
   Method2() string
}

type Test struct{}

func (t Test) Method1() string {
   return "Method1"
}

func (t Test) Method2() string {
   return "Method2"
}

func main() {
   test := Test{}
   // test既是I1接口实例
   t1(test)
   // test又是I2接口实例
   t2(test)
}

// 需要I1接口实例
func t1(i1 I1) {
   fmt.Println("t1...", i1.Method1())
}

// 需要I2接口实例
func t2(i2 I2) {
   // 可以看到I2也有Method1方法
   fmt.Println("t2...", i2.Method1())
   fmt.Println("t2...", i2.Method2())
}

实现多态

示例:

type Animal interface {
   // 物种
   Type() string
}

type Cat interface {
   Animal
   // 猫的颜色
   Color() string
}

type WhiteCat struct{}

func (wc WhiteCat) Type() string {
   return "猫科动物"
}

func (wc WhiteCat) Color() string {
   return "白色"
}

type BlackCat struct{}

func (bc BlackCat) Type() string {
   return "猫科动物"
}

func (bc BlackCat) Color() string {
   return "黑色"
}

func main() {
   whiteCat := WhiteCat{}
   blackCat := BlackCat{}
   catColor(whiteCat) // cat color: 白色
   catColor(blackCat) // cat color: 黑色
}

func catColor(cat Cat) {
   fmt.Println("cat color:", cat.Color())
}

可以看到,不同的猫Color方法的实现不同。

模拟构造函数

最佳实践:
通常会定义NewXXX方法来创建一个结构体,NewXXX方法通常会返回结构体指针,如下:

type Person struct {
}

func NewPerson() *Person {
   return &Person{}
}

func main() {
   person := NewPerson()
   fmt.Printf("person:%v\n", person)
}