【Go】面向对象编程的实现

Go 面向对象编程的实现

Go 语言没有类(class)的概念,但这并不意味着 Go 语言不支持面向对象编程,毕竟面向对象只是一种编程思想。面向对象有三大基本特征:

  • 封装:隐藏对象的属性和实现细节,仅对外提供公共访问的方式
  • 继承:使得子类具有父类的属性和方法或者重新定义、追加属性和方法等
  • 多态:不同对象中同种行为的不同实现方式

一、封装

1. 属性

Go 语言中可以使用结构体对属性进行封装。结构体就像是类的一种简化方式。例如,我们要定义一个三角形,每个三角形都有底和高,可以这样进行封装:

type Triangle struct {
    
    
    Bottom float32
    Height float32
}

2. 方法

既然有了“类”,那方法呢?Go 语言也有方法(Methods),方法是作用在接收者(receiver)上的一个函数,接收者是某种类型的变量。因此,方法是一种特殊类型的函数。

定义方法的格式如下:

func (recv recv_type) methodName (param_list) (return_value_list) {...}

我们已经定义了一个三角形类,现在我们为其定义一个Area() 方法计算其面积:

package main

import "fmt"

// Triangle 三角形的结构体
type Triangle struct {
    
    
	Bottom float32
	Height float32
}

// Area 计算三角形的面积
func (t *Triangle) Area() float32 {
    
    
	return (t.Bottom * t.Height) / 2
}

func main() {
    
    
	r := Triangle{
    
    
		Bottom: 6,
		Height: 8,
	}
	fmt.Println(r.Area())
}

运行结果为 24

3. 访问权限

在 C++ / Java 等语言中,使用 publicprivate 等关键字来表达访问权限,在 Go 语言中,是通过字母的大小写来控制可见性

  • 如果定义的常量、变量、类型、接口、结构体、函数等的名称是大写字母打头,则表示他们能被其他包访问或调用(相当于 public);
  • 非大写开头就只能在包内使用(相当于 private)

例如,定义一个学生结构体来描述名字和分数:

type Student struct {
    
    
    name string
    score float32
    Age int
}

在以上结构体中,Age 属性可以直接被其他包访问,而 name 就不行。

和其他面向对象语言一样,Go 语言也有实现获取和设置属性的方式:

package main

type Student struct {
    
    
	name string
	score float32
}

// 获取 name
func (s *Student) GetName() string {
    
    
	return s.name
}

// 设置 name
func (s *Student) SetName(newName string) {
    
    
	s.name = newName
}

二、继承

Go 语言没有 extends 关键字,而是使用结构体中内嵌匿名类型的方法来实现继承。例如定义一个 Engine 接口类型和一个 Bus 结构体,让 Bus 结构体包含一个 Engine 接口的匿名字段:

package main

type Engine interface {
    
    
	Run()
	Stop()
}

type Bus struct {
    
    
	Engine  // 包含 Engine 类型的匿名字段
}

func (c *Bus) Working() {
    
    
	c.Run()
	c.Stop()
}

此时,匿名字段 Engine 上的方法“晋升”为外层类型 Bus 的方法。

三、多态

在面向对象中,多态的特征是不同对象中的同种行为的不同实现方式。在 Go 语言中,可以通过使用接口来实现这个特征。

先定义一个正方形 Square 和一个三角形 Triangle:

// Square 正方形
type Square struct {
    
    
	sideLen float32
}

// Triangle 三角形
type Triangle struct {
    
    
	Bottom float32
	Height float32
}

然后,希望可以计算出这两个几何图形的面积。但由于他们的面积计算方式不一样,所以需要定义两个不同的Area() 方法。

于是定义一个包含 Area() 方法的接口 Shape,让 Square 和 Triangle 都实现这个接口里的 Area() 方法:

// Area 计算三角形的面积
func (t *Triangle) Area() float32 {
    
    
	return (t.Bottom * t.Height) / 2
}

// Shape 接口
type Shape interface {
    
    
	Area() float32
}

// Area 计算正方形的面积
func (sq *Square) Area() float32 {
    
    
	return sq.sideLen * sq.sideLen
}

func main() {
    
    
	t := &Triangle{
    
    6, 8}
	s := &Square{
    
    8}
	shapes := []Shape{
    
    t, s}
	for i, _ := range shapes {
    
    
		fmt.Println("图形数据:", shapes[i])
		fmt.Println("它的面积是:", shapes[i].Area())
	}
}

以上代码的运行结果为:

图形数据: &{
    
    6 8}
它的面积是: 24
图形数据: &{
    
    8}
它的面积是: 64

由以上代码输出结果可知,不同对象调用 Area() 方法产生了不同的结果,展现了多态的特征。

猜你喜欢

转载自blog.csdn.net/qq_45668004/article/details/118024783