golang学习【8】:面向对象与面向过程

面向对象与面向过程

面向对象

活在当下的程序员应该都听过"面向对象编程"一词,也经常有人问能不能用一句话解释下什么是"面向对象编程",我们先来看看比较正式的说法。

"把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。"

这样一说好像更抽象了,我们一步步解析。

面向对象是什么
  • 对象是什么
    • 对象是现实世界实体的抽象,比如我们可以把某只猫作为一个对象,某只狗也是一个对象;这里指的是一个具体的实体,比如你家里养了猫叫小红小红是对象,属于分类
  • 分类是什么
    • 分类是对一组相似对象的归纳,我们把统称为动物,那么动物就是一个分类;当然了,我们也可以把看成一个分类,然后哈士奇金毛是一个子类;家里养的金毛是对象
  • 封装是什么
    • 比如是会做这个动作的,产生这个动作的需要经过其神经和肌肉的协作协调,封装就是把这个协调过程隐藏了;就如我们看做出这个动作,并不会了解其内部神经和肌肉的协调过程
  • 继承(泛化,特化,多态)是什么
    • 都属于动物动物是会进行呼吸的,那么也会呼吸,这就是继承,也是泛化;但是的呼吸方式是不同的,它们的内部细节不同,就属于多态除了呼吸,还具备跳跃这种有其他动物没有的,叫特化
  • 面向对象是什么
    • 面向对象是一种编程范式或编程方法论,就是一种设计理念和方法;
为什么要用面向对象

之前我们说过"程序是指令的集合",我们在程序中书写的语句在执行时会变成一条或多条指令然后由CPU去执行。当然为了简化程序的设计,我们引入了函数的概念,把相对独立且经常重复使用的代码放置到函数中,在需要使用这些功能的时候只要调用函数即可;如果一个函数的功能过于复杂和臃肿,我们又可以进一步将函数继续切分为子函数来降低系统的复杂性。但是说了这么多,不知道大家是否发现,所谓编程就是程序员按照计算机的工作方式控制计算机完成各种任务。但是,计算机的工作方式与正常人类的思维模式是不同的,如果编程就必须得抛弃人类正常的思维方式去迎合计算机,编程的乐趣就少了很多,"每个人都应该学习编程"这样的豪言壮语就只能说说而已。当然,这些还不是最重要的,最重要的是当我们需要开发一个复杂的系统时,代码的复杂性会让开发和维护工作都变得举步维艰,所以在上世纪60年代末期,"软件危机"、"软件工程"等一系列的概念开始在行业中出现。

当然,程序员圈子内的人都知道,现实中并没有解决上面所说的这些问题的"银弹",真正让软件开发者看到希望的是上世纪70年代诞生的Smalltalk编程语言中引入的面向对象的编程思想(面向对象编程的雏形可以追溯到更早期的Simula语言)。按照这种编程理念,程序中的数据和操作数据的函数是一个逻辑上的整体,我们称之为“对象”,而我们解决问题的方式就是创建出需要的对象并向对象发出各种各样的消息,多个对象的协同工作最终可以让我们构造出复杂的系统来解决现实中的问题。

说明: 当然面向对象也不是解决软件开发中所有问题的最后的“银弹”,所以今天的高级程序设计语言几乎都提供了对多种编程范式的支持,go也不例外。

使用面向对象

简单的说,类是对象的蓝图和模板,而对象是类的实例。这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。

在 Go 语言中,虽然没有传统的“类”概念,但通过使用结构体(structs)、方法(methods)以及接口(interfaces),可以实现面向对象编程的特性,如封装、组合(而非继承)和多态。以下是一个面向对象编程的例子,展示如何在 Go 中定义结构体、为其添加方法,并使用接口实现多态性

定义类(结构体)

先来看结构体概念,一个结构体(struct)就是一组字段(field)

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
    //结构体字段使用点号来访问。
	v := Vertex{1, 2}
	v.X = 4
    fmt.Println(v.X)
    //也可以通过指针访问
    p := &v
	p.X = 1e9
	fmt.Println(v.X)
}

用结构体来实现类的定义

package main

type Animal struct {
    Name string
    Age  int
}

// Speak 方法模拟动物发声的行为
func (a Animal) Speak() string {
    return "This animal makes an ambiguous sound."
}

// Info 方法返回动物的基本信息
func (a Animal) Info() string {
    return fmt.Sprintf("Animal: %s, Age: %d", a.Name, a.Age)
}

说明: 定义结构体的函数(方法),在函数名前用()来关联结构体,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息。

实现继承

go的继承通过在结构体中嵌入来实现继承

type Dog struct {
    Animal // 匿名嵌入 Animal 结构体
    Breed  string
}

// Speak 方法覆盖 Animal 的同名方法,为 Dog 提供具体的叫声
func (d Dog) Speak() string {
    return fmt.Sprintf("%s barks!", d.Name)
}
实现多态

golang 通过interface来实现多态

type Speaker interface {
    Speak() string
}
创建和使用对象

当我们定义好一个类之后,可以通过下面的方式来创建对象并给对象发消息。

func main() {
    // 创建 Animal 实例
    cat := Animal{Name: "Whiskers", Age: 3}
    println(cat.Speak())   // 输出:This animal makes an ambiguous sound.

    // 创建 Dog 实例
    rover := Dog{Animal: Animal{Name: "Rover", Age: 5}, Breed: "Golden Retriever"}
    println(rover.Speak())  // 输出:Rover barks!

    // 将实例赋值给 Speaker 接口类型变量,实现多态
    var speakers []Speaker
    speakers = append(speakers, cat)
    speakers = append(speakers, rover)

    // 通过 Speaker 接口调用 Speak() 方法,无需关心具体类型
    for _, speaker := range speakers {
        println(speaker.Speak())
    }
}
访问可见性问题

在 Go 语言中,结构体的字段和方法的访问可见性(也称为访问权限)由其首字母的大小写决定。Go 语言遵循“导出规则”(Export rule),简单来说就是:

  • 大写字母开头:字段或方法被视为公开的(public),可以在包外访问。这是导出(exported)的标志。
  • 小写字母开头:字段或方法被视为私有的(private),只能在包内访问。这是未导出(unexported)的标志。
type Person struct {
    name string // 私有字段,只能在定义它的包内访问
    age  int    // 私有字段,只能在定义它的包内访问

    FirstName string // 公开字段,可以在包内外访问
    LastName  string // 公开字段,可以在包内外访问
}


// 私有方法,只能在定义它的包内调用
func (p Person) fullName() string {
    return p.FirstName + " " + p.LastName
}

// 公开方法,可以在包内外调用
func (p Person) FullName() string {
    return p.FirstName + " " + p.LastName
}

golan严格保证私有属性或方法的私密性,旨在保护包的内部实现细节,确保外部代码只能通过公开(导出)的接口与包进行交互,从而提高代码的封装性和稳定性。

面向对象的支柱

面向对象有三大支柱:封装、继承和多态。这一章节已经对三者作了简单说明示例。

面向过程

和面向对象一样,面向过程是一种编程范式或编程方法论,它以过程(或称函数、子程序)作为组织和解决问题的基本单元。面向过程编程强调将一个复杂的任务分解成一系列可执行的步骤(过程),每个步骤定义为一个独立的函数,函数之间通过参数传递数据,按照预定的顺序执行,最终完成整个任务。我们前面几章的学习和练习均属于面向过程的方法。

面向过程和面向对象对比

对于golang,两者对比如下

特性/范式面向过程面向对象
基本单元函数结构体
数据与操作的组织数据与函数分离数据与行为封装
抽象级别较低级抽象较高级抽象
代码组织基于过程的模块化基于对象的模块化
复用机制函数复用结构体/接口复用
控制流过程调用与控制结构消息传递
问题建模基于任务分解基于实体与行为
典型应用领域简单系统、嵌入式系统、性能关键型软件、脚本编写复杂系统、企业级应用、图形用户界面开发、游戏开发

练习

练习1:定义一个类描述数字时钟。

参考答案:

package main

import (
	"fmt"
	"time"
)

// Clock 结构体代表一个数字时钟
type Clock struct{
    hour int
    miniute int
    second int
}

// NewClock 返回一个新的 Clock 实例,初始化时间为当前时间
func NewClock() *Clock {
	now := time.Now()
	return &Clock{
		hour:   now.Hour(),
		minute: now.Minute(),
		second: now.Second(),
	}
}

// Tick 模拟时钟每秒走动一次
func (c *Clock) Tick() {
	c.second++
	if c.second >= 60 {
		c.second = 0
		c.minute++
		if c.minute >= 60 {
			c.minute = 0
			c.hour = (c.hour + 1) % 24
		}
	}
}

// Display 打印当前时钟的时间
func (c *Clock) Display() {
	fmt.Printf("%02d:%02d:%02d\n", c.hour, c.minute, c.second)
}

func main() {
	clock := NewClock()

	for {
		clock.Tick()
		clock.Display()
		time.Sleep(time.Second)
	}
}
  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值