当前位置: 首页 > news >正文

go语言进阶篇——面向对象(一)

什么是面向对象

在我们设计代码时,比如写一个算法题或者写一个问题结局办法时,我们常常会使用面向过程的方式来书写代码,面向过程主要指的是以解决问题为中心,按照一步步具体的步骤来编写代码或者调用函数,他在问题规模小的情况下简洁快速且十分有效。

当我们遇到的问题比较庞大且复杂的时候,面向过程的代码就会变得难以维护与重复使用,这时我们就需要对问题进行抽象,将一类具有鲜明特色的函数抽象为一类对象,我们只需要通过调用对象中的一个方法来处理某一类特定的问题,对象与对象之间用方法来交互,这种编程方法我们称之为面向对象编程。

面向对象主要的特征有三点:封装,继承,多态,它们在面向对象编程中占据着重要的地位,最后,开始今天的学习之前我们要明确一件事情:面向对象只是一种思想,它并不特指某一部分语言

Go语言的面向对象编程设计

前言

相对于传统的如c++,Java等语言而言,Go语言显得优雅且简洁,它没有去沿袭传统面向对象编程得诸多概念,比如类的继承,接口的实现,构造与析构函数等等,也不再有publicprivateprotected等访问修饰符

Go语言的优雅之处,它对面向对象编程的支持是语言类型系统中的天然组成部分,整个类型系统通过接口串联,浑然一体

什么是类型系统

类型系统指的是一个语言的类型体系结构,一个典型类型系统一般包括以下内容:

  1. 基本类型(int,string ,byte,float)
  2. 复合类型(数组,切片,字典,字符串)
  3. 可以指向任意对象的类型(比如Go语言中的空接口)
  4. 值语义与引用语义(值语义指的是数据类型在赋值时会生成副本,彼此之间互不干扰,引用语义主要是多个副本共享一份数据,修改任意一个其他的也会随之修改)
  5. 面向对象,即所有具备面向对象特征(比如成员方法)的类型
  6. 接口

Java VS Go类型系统设计

Java

在Java语言中,存在两种完全独立的类型系统、

  1. 值类型系统,这里主要是基本类型,如int ,float,double等等
  2. Object类型为根的对象类型系统,它可以定义成员变量,成员方法,虚函数,这些一般是引用语义,在堆上分配内存

Java 语言中的 Any 类型就是整个对象类型系统的根 —— java.lang.Object 类型,只有对象类型系统中的实例才可以被 Any 类型引用。值类型想要被 Any 类型引用,需要经过装箱 (boxing)过程,比如 int 类型需要装箱成为 Integer 类型。

另外,在 Java 中,只有对象类型系统中的类型才可以实现接口。

Go

Go语言的大多数类型都是值语义,比如基本类型(int,float,double等等)或者是复合类型(数组,结构体等)

类的定义,初始化以及成员方法

类的定义与初始化

Go语言的面向对象编程与我们熟悉的Java等语言不同,它没有像classimplements,extend之类的关键字以及相应的概念,主要还是依靠i结构体来实现的,比如我们现在像创建一个学生类:

type Student struct{id stingname string age uint sex string
}

类名为student,并且包括了idnameagesex四个属性,Go语言也不支持构造函数与析构函数

所以我们可以定义全局函数NewStudent来初始化

func NewStudent(id,nmae,sex string.age uint) *Student{return &Strudent{id,nmae,sex,age}
}

当然我们也可以初始化指定字段:

func NewStudent1(age int,id,name string) *Student{return &Student{id:   id,name: name,age:  age,}
}

在 Go 语言中,未进行显式初始化的变量都会被初始化为该类型的零值,例如 bool 类型的零值为 falseint 类型的零值为 0,string 类型的零值为空字符串,float 类型的零值为 0.0

定义成员方法

值方法

由于 Go 语言不支持 class 这样的代码块,要为 Go 类定义成员方法,需要在 func 和方法名之间声明方法所属的类型(有的地方将其称之为接收者声明),以 Student 类为例,要为其定义获取 name 值的方法,可以这么做:

func (s Student) GetName(string {return	s.name}

这样一来,我们就可以在初始化 Student 类后,通过 GetName() 方法获取 name 值:

student := NewStudent(1, "学院君", 100)fmt.Println("Name:", student.GetName())

可以看到,我们通过在函数签名中增加接收者声明的方式定义了函数所归属的类型,这个时候,函数就不再是普通的函数,而是类的成员方法了。

指针方法

在类的成员方法中,可以通过声明的类型变量来访问类的属性和其他方法(Go 语言不支持隐藏的 this 指针,所有的东西都是显式声明)。GetName 是一个只读方法,如果我们要在外部通过 Student 类暴露的方法设置 name 值,可以这么做:

func (s *Student) SetName(name string) {s.name = name}

你可能已经注意到,这里的方法声明和前面 GetXXX 方法声明不太一样,Student 类型设置成了指针类型:

s *Student

这是因为 Go 语言面向对象编程不像 PHP、Java 那样支持隐式的 this 指针,所有的东西都是显式声明的,在 GetXXX 方法中,由于不需要对类的成员变量进行修改,所以不需要传入指针,而 SetXXX 方法需要在函数内部修改成员变量的值,并且该修改要作用到该函数作用域以外,所以需要传入指针类型(结构体是值类型,不是引用类型,所以需要显式传入指针)。

我们可以把接收者类型为指针的成员方法叫做指针方法,把接收者类型为非指针的成员方法叫做值方法,二者的区别在于值方法传入的结构体变量是值类型(类型本身为指针类型除外),因此传入函数内部的是外部传入结构体实例的值拷贝,修改不会作用到外部传入的结构体实例

选择值方法还是指针方法

当我们有如下情形的考量时,需要将类方法定义为指针方法:

  1. 数据一致性:方法需要修改传入的类型实例本身;
  2. 方法执行效率:如果是值方法,在方法调用时一定会产生值拷贝,而大对象拷贝代价很大。

通常我们都会选择定义指针方法。

基于组合来实现类的继承与方法重写

要实现面向对象编程,就必须实现面向对象编程的三大特性:封装、继承和多态。上面我们已经介绍了类的封装,将函数定义为归属某个自定义类型,这就等同于实现了类的成员方法,如果这个自定义类型是基于结构体的,那么结构体的字段可以看做是类的属性。

继承

在go语言中并没有直接的提供有关继承的与凡是线,但是我们可以使用组合的方式来简介实现继承功能。

传统面向对象编程中,显式定义继承关系的弊端有两个:一个是导致类的层级越来越复杂,另一个是影响了类的扩展性,很多软件设计模式的理念就是通过组合来替代继承提高类的扩展性。减下来我们通过几个例子来看一下如何利用组合来实现继承

首先我们可以来定义一个父类Animal

type Animal struct {Name string
}func (a *Animal) Call() string{return "动物的叫声是..."
}func (a *Animal) FavorFood() string{return "动物最喜欢的食物是..."
}func (a *Animal) GetName(name string) string{a.Name = name
}

如果我们想到一个子类Dog,可以这么来写:

type Dog struct{Animal//other things
}

这里,我们在 Dog 结构体类型中,嵌入了 Animal 这个类型,这样一来,我们就可以在 Dog 实例上访问所有 Animal 类型包含的属性和方法,相当于通过组合实现了继承

多态

在go语言中我们可以通过字子类中定义同名方法来覆盖父类方法,比如我们现在重写一下Animal中的方法:

func (d *Dog) Call() string{return "汪汪汪"
}func (d *Dog) FavorFood() string{return "骨头"
}

当我们再执行 main 函数时,直接在 Dog 实例上调用 Call 方法或 FavorFood 方法时,调用的就是 Dog 类中定义的方法而不是 Animal 中定义的方法,如果要指定调用Animal里面的函数,就要按照下面的格式:

dog.Animal.Call()

拓展

可以看到,与传统面向对象编程语言的继承机制不同,这种组合的实现方式更加灵活,我们不用考虑单继承还是多继承,你想要继承哪个类型的方法,直接组合进来就好了。接下来我们来介绍一下继承与多态中常出现的一些问题:

  • 多继承同名方法冲突处理

    如果组合中不同类型中包含同名的方法,比如下面这种情况:

    type Dog struct{Animalpet
    }
    

    如果Animal和pet中有同名方法且类 Dog 没有重写该方法,直接在 Dog 实例上调用的话会报错,除非我们指定了执行哪个父类的函数

  • 调整组合位置会改变内存布局

    另外,我们还可以通过任意调整被组合类型的位置来改变类的内存布局:

    type Dog struct {AnimalPet
    }
    

    type Dog struct {PetAnimal
    }
    

    虽然上面两个 Dog 子类的功能一致,但是它们的内存结构不同。

  • 为组合类型设置别名

    前面的示例调用父类方法时都直接引用的是组合类型(父类)的类型字面量,其实,我们还可以像基本类型一样,为其设置别名,方便引用:

    type Dog struct{pet *Petanimal *Animal 
    }
    

类属性和成员方法可见性设置

在go语言中,无论是变量,函数还是类属性和成员方法,它们的可见性都是以包为维度的,go没有像publicprivateprotected这样的关键字来修饰其可见性。它们的可见性都是根据其首字母的大小写来决定的,如果变量名、属性名、函数名或方法名首字母大写,就可以在包外直接访问这些变量、属性、函数和方法,否则只能在包内访问,因此 Go 语言类属性和成员方法的可见性都是包一级的,而不是类一级的。

接下来我们来演示一个例子:

首先我们来创建一个animal包,然后创建一个animal.go文件:

package animaltype Animal struct {Name string
}func (a Animal) Call() string {return "动物的叫声..."
}func (a Animal) FavorFood() string {return "爱吃的食物..."
}func (a Animal) GetName() string  {return a.Name
}

然后再创建一个pet.go

package animaltype Pet struct {Name string
}func (p Pet) GetName() string  {return p.Name
}

然后创建dog.go

package animaltype Dog struct {Animal *AnimalPet Pet
}func (d Dog) FavorFood() string {return "骨头"
}func (d Dog) Call() string {return "汪汪汪"
}

最后是main.go文件

package mainimport ("fmt". "animal"
)func main() {animal := Animal{Name: "中华田园犬"}pet := Pet{Name: "宠物狗"}dog := Dog{Animal: &animal, Pet: pet}fmt.Println(dog.Animal.GetName())fmt.Print(dog.Animal.Call())fmt.Println(dog.Call())fmt.Print(dog.Animal.FavorFood())fmt.Println(dog.FavorFood())
}

这样我们就实现了一个简单的面向对象程序,但是这里文件的变量以及方法名字都是大写,类似于全部都是public(公有的)如果你觉得直接暴露这三个类的所有属性可以被任意修改,不够安全,还可以通过定义构造函数来封装它们的初始化过程,然后把属性名首字母小写进行私有化:

animal.go为例

package animaltype Animal struct {name string
}func NewAnimal(name string) Animal {return Animal{name: name}
}func (a Animal) Call() string {return "动物的叫声..."
}func (a Animal) FavorFood() string {return "爱吃的食物..."
}func (a Animal) GetName() string  {return a.name
}

此时运行程序就会:
在这里插入图片描述

总结:

上面我们介绍了go语言的类型系统,并且完成了使用go语言来实现一个简单面向对象封装,继承与多态,大家可以多西靠思考,理解一下go语言的面向对象与常见如c++,Java等语言再面向对象实现上的不同,后面博主将介绍有关于接口在面向对象中的使用,以及有关泛型的使用以及基于泛型来实现我们自己封装的简短的数据结构,大家下篇见!,最后的最后,大家如果喜欢,还请收藏加关注,这样才能不迷路哦!!!
在这里插入图片描述

相关文章:

go语言进阶篇——面向对象(一)

什么是面向对象 在我们设计代码时,比如写一个算法题或者写一个问题结局办法时,我们常常会使用面向过程的方式来书写代码,面向过程主要指的是以解决问题为中心,按照一步步具体的步骤来编写代码或者调用函数,他在问题规…...

C#,栅栏油漆算法(Painting Fence Algorithm)的源代码

1 刷油漆问题 给定一个有n根柱子和k种颜色的围栏,找出油漆围栏的方法,使最多两个相邻的柱子具有相同的颜色。因为答案可以是大的,所以返回10^97的模。 计算结果: 2 栅栏油漆算法的源程序 using System; namespace Legalsoft.Tr…...

java_error_in_pycharm.hprof文件是什么?能删除吗?

java_error_in_pycharm.hprof文件是什么?能删除吗? 🌵文章目录🌵 🌳引言🌳🌳hprof格式文件介绍🌳🌳java_error_in_pycharm.hprof文件什么情况下能删除🌳&…...

LeetCode 491 递增序列

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。 示例: 输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]说明: 给定数组的长度不会超过15。 数组中的整数范围是 [-…...

考研/计算机二级数据结构刷题之顺序表

目录 第一题 顺序表的初始化,销毁,头插,尾插,头删,尾删,指定位置插入,指定删除以及打印 第二题 移除元素 题目链接: OJ链接 题目详解:移除元素 第三题:删…...

Git 代码协同的使用方法 for Azure DevOps

1. 登陆Azure 步骤1.1. VS Code,登陆Azure Cloud的Ubuntu环境,如下: 重点: 这里的Azure Cloud的用户名是YourAzureUserName,口令是YourAzurePassword 步骤1.2. 登陆Azure Cloud的Ubuntu环境后,配置Git账户信息&…...

数据库学习笔记2024/2/5

2. SQL 全称 Structured Query Language,结构化查询语言。操作关系型数据库的编程语言,定义了 一套操作关系型数据库统一标准 2.1 SQL通用语法 在学习具体的SQL语句之前,先来了解一下SQL语言的通用语法。 1). SQL语句可以单行或多行书写&…...

PSM-Net根据Stereo图像生成depth图像

一、新建文件夹 在KITTI数据集下新建depth_0目录 二、激活anaconda环境 conda activate pt14py37三、修改submission.py文件 3.1 KITTI数据集路径 parser.add_argument(--datapath, default/home/njust/KITTI_DataSet/00/, helpselect model)3.2 深度图像输出路径 save…...

Mocaverse NFT 概览与数据分析

作者:stellafootprint.network 编译:mingfootprint.network 数据源:Mocaverse NFT Collection Dashboard Mocaverse 是 Animoca Brands 推出的专属 NFT(非同质化代币)系列,包含 8,888 个独特的 "M…...

SpringBoot之事务源码解析

首先事务是基于aop的,如果不了解aop的,建议先去看下我关于aop的文章: Spring之aop源码解析  先说结论,带着结论看源码。首先,在bean的生命周期中, 执行实例化前置增强,会加载所有切面并放入缓存&#xff0…...

FPGA高端项目:解码索尼IMX327 MIPI相机转USB3.0 UVC 输出,提供FPGA开发板+2套工程源码+技术支持

目录 1、前言免责声明 2、相关方案推荐我这里已有的 MIPI 编解码方案 3、本 MIPI CSI-RX IP 介绍4、个人 FPGA高端图像处理开发板简介5、详细设计方案设计原理框图IMX327 及其配置MIPI CSI RX图像 ISP 处理图像缓存UVC 时序USB3.0输出架构FPGA逻辑设计工程源码架构SDK软件工程源…...

基于高通滤波器的ECG信号滤波及心率统计matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 ECG信号简介 4.2 高通滤波器原理 4.3 心率统计 5.完整工程文件 1.课题概述 通过高通滤波器对ECG信号进行滤波,然后再统计其心率。 2.系统仿真结果 3.核心程序与模型 版本&#xff1a…...

springCould中的gateway-从小白开始【9】

目录 1.🍟网关是什么 2.🍿gateway是什么 3.🥚gateway能什么 4.🌭核心概念 5.🧂工作流程 6.🧈实例 7.🥓gateway网关配置的方式 8.🍳配置动态路由 9.🧇pred…...

邮箱营销软件推荐?企业邮箱群发会限制吗?

邮箱营销平台怎么选择?哪种邮箱适合做外贸邮件群发? 邮箱营销凭借其精准触达、低成本和高回报的特点,依然是许多企业不可或缺的营销手段。该如何选择一款适合自己的工具呢?蜂邮EDM将为您推荐几款优秀的邮箱营销软件,并…...

编译原理实验3——自下而上的SLR1语法分析实现(包含画DFA转换图、建表、查表)

文章目录 实验目的实现流程定义DFA状态实现代码运行结果测试1测试2测试3 总结 实验目的 实现自下而上的SLR1语法分析,画出DFA图 实现流程 定义DFA状态 class DFA:def __init__(self, id_, item_, next_ids_):self.id_ id_ # 编号self.item_ item_ # productio…...

基于tomcat的https(ssl)双向认证

一、背景介绍 某个供应商服务需要部署到海外,如果海外多个地区需要部署多个服务,最好能实现统一登录,这样可以减轻用户的使用负担(不用记录一堆密码)。由于安全问题(可能会泄露用户数据)&#x…...

【iOS ARKit】3D人体姿态估计实例

与2D人体姿态检测一样,在ARKit 中,我们不必关心底层的人体骨骼关节点检测算法,也不必自己去调用这些算法,在运行使用 ARBodyTrackingConfiguration 配置的 ARSession 之后,基于摄像头图像的3D人体姿态估计任务也会启动…...

ROS2 CMakeLists.txt 和 package.xml

这里记录一下ROS2中功能包package.xml和CMakeLists.txt的格式。以LIO-SAM的ROS2版本为例: 一:CMakeLists.txt cmake_minimum_required(VERSION 3.5) project(lio_sam)if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)set(CMAKE_BUILD_TYPE…...

代码献瑞,算力有礼!低代码开发工具PaddleX特色产线新春福利来啦

回望2023年,飞桨在开发套件能力基础上,充分结合大模型能力,正式在飞桨星河社区上线发布了低代码开发工具PaddleX,实现AI应用开发效果和效率的大幅提升。产品通过提供图形界面开发模式,将复杂的编程任务简化为简单易用的…...

C语言:操作符详解

创作不易,给个三连吧!! 一、算术操作符 C语言中为了方便计算,提供了算数操作符,分别是:,-,*,/,% 由于这些操作符都是有两个操作数(位于操作符两边),所以这种操作符也叫做双目操作…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

云计算——弹性云计算器(ECS)

弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...