通过 GoLang 实现面向对象思想

2022-06-27 14:10:01 浏览数 (1)

1. 引言

面向对象是一种十分流行的编程思想,他改变了传统的面向过程编程思想看待问题的方式,先将所有实体定义为对象,然后为对象添加各自必须的属性和动作,从而让程序在设计的过程中,更为方便的抽取出实体的共性与差异,实现组件的替换以及更为复杂的各个设计模式,从而实现程序设计的高内聚与低耦合,进而提升程序的重用性、灵活性、扩展性、可读性和安全性。 面向对象思想并不基于某个编程语言,例如在典型的面向过程语言 C 语言中,通过结构体、void 指针、函数指针等语法,我们就完全可以实现出面向对象的语法特征,事实上,nginx 正是使用 C 语言的这些特性,通过面向对象的思想来组织整个工程项目的。 golang 语言中并没有明确提出面向对象的概念,但基于 golang 已有的语法,我们同样可以实现面向对象的思想,本文我们就来详细介绍一下。

2. 面向对象的特征

面向对象拥有三个基本特征:

  1. 封装
  2. 继承
  3. 多态

3. golang 的封装

GoLang 中的封装主要依赖结构体实现对象的封装:

代码语言:javascript复制
package person

import "fmt"

type Person struct {
    Name string
    Age int
}

func (person *Person) ShowInfo() {
    fmt.Printf("My name is %s, age is %dn", person.Name, person.Age)
}

func (person *Person) SetAge(age int) {
    person.Age = age
}

上述代码封装了 Person 类,以及他的成员变量 Name、Age 和两个成员方法。

4. 继承

4.1. 通过组合封装实现继承

golang 中并没有继承的语法特性,但通过组合我们可以实现一部分面向对象中的继承特性。

代码语言:javascript复制
package person

import "fmt"

type Student struct {
    Person
    Id int
    Score int
}

func (student *Student) ShowInfo() {
    fmt.Printf("I am a student. My name is %s, age is %dn", student.Name, student.Age)
}

func (student *Student) read() {
    fmt.Println("read book")
}

4.2. 实际使用

在 main 函数中,我们可以使用:

代码语言:javascript复制
package main

import (
    "../person"
    "unsafe"
)

func showPersonInfo(person *person.Person) {
    person.ShowInfo()
}

func main() {
    link := person.Student{person.Person{Name: "Link", Age: 7}, 1, 10}
        link.ShowInfo()
    showPersonInfo((*person.Person)(unsafe.Pointer(&link)))
}

展示出了:

I am a student. My name is Link, age is 7 My name is Link, age is 7

4.3. 限制

上述代码虽然实现了面向对象思想中,继承的一部分特性,但仍然存在较多限制:

  1. 虽然一个结构体内部也可以同时组合多个不同的结构体,但由于 GoLang 指针不能移动的特性,我们只能将指向当前结构体的指针强制转换为指向首个父类的指针
  2. 由于 GoLang 中严格的类型检测,子类与父类由于类型不同,在使用中有着严格限制,难以有效转换
  3. 一旦一个结构体中拥有了匿名的成员,就不能通过指定成员来初始化结构体实例了,这通常是不够安全的,尤其在结构体成员很多时;而如果不使用匿名成员来组合“父类”,在使用中又显得过于繁琐

5. 多态

多态指的是同一个行为具有多个不同表现形式或形态的能力,简单的说,就是同一个接口,使用不同的实例而执行不同的操作。 实际上,上述示例中,已经展示出了多态特性,那就是虽然 Student 结构体继承自 Person,但其 ShowInfo 覆盖了父类的相应操作,实现了实例自身操作的实现。

5.1. 限制

在上面的例子中,我们同样可以看出,GoLang 语言实现多态特性仍然存在着比较大的限制。 我们不能将子类实例传递给需要父类实例作为参数的方法中,虽然可以通过 unsafe.Pointer 类进行强制类型转换,但转换后已经不再是子类类型,无法通过转换后的指针实现多态特性。

5.2. 接口类 — interface

在 GoLang 中,我们也可以定义 interface 类型,仅声明方法,由所有子类进行实现:

代码语言:javascript复制
package main

import "fmt"

type Human interface {
    speak(language string)
}

type Chinese struct {

}

type American struct {

}

func (ch Chinese) speak(language string ) {
  fmt.Printf("speak %sn",language)
}

func (am American ) speak(language string ) {
  fmt.Printf("speak %sn",language)
}

func main() {
    var ch Human
    var am Human

    ch = Chinese{}
    am = American{}

    ch.speak("Chinese")
    am.speak("English")
}

在这个例子中,多态特性显得更为清晰。

6. 总结

通过上面的例子,希望读者能够认识到,在面向对象思想的基础上,某种特定的编程语言可以通过其自身的语法,让用户在使用中更为灵活地运用思想本身的各项特性,但面向对象编程毕竟只是一种编程思想,并非与某种语言绑定的语言特性,C 语言、GoLang 等并非原生支持面向对象思想的语言都可以在不同程度上实现面向对象编程的特性。 但与 C 语言相比,GoLang 严格的类型检验与无法移动的指针类型以及精简的语言特性,让他在面向对象思想的实现中更加捉襟见肘,但从另一个角度考虑,这些 GoLang 语言本身的限制,让 GoLang 语言更加安全且易用。 总而言之,编程语言是实践中的一种工具,并非打开全世界所有大门的万能钥匙,我们能做的只有在权衡利弊,在特定语言语法所给出的有限灵活空间内发挥自己的想象,解决一个又一个实际的问题,来找到打开新世界大门的那把钥匙。

0 人点赞