Golang面向对象编程(二)

文章目录

  • 封装
    • 基本介绍
    • 封装的实现
    • 工厂函数
  • 继承
    • 基本介绍
    • 继承的实现
    • 字段和方法访问细节
    • 多继承

封装

基本介绍

基本介绍

  • 封装(Encapsulation)是面向对象编程(OOP)中的一种重要概念,封装通过将数据和相关的方法组合在一起,形成一个称为类的抽象数据类型,只暴露必要的接口供外部使用。
  • 封装可以隐藏数据的实际实现细节,外部只能通过公共(public)接口来访问和修改数据,使得代码更加模块化和结构化,同时可以防止不恰当的访问和操作,提高数据的安全性。
  • 封装将相关的数据和方法组织在一起,形成了一个独立的单元,外部使用者只需关心公共接口,无需了解内部实现细节,简化了使用方式,提高了代码的可读性和可维护性。
  • 封装使得内部实现可以独立于外部接口进行修改,如果内部实现发生了变化,只需要确保公共接口的兼容性,而不会影响使用该类的其他代码,提供了更好的灵活性和可扩展性。

封装的实现

封装的实现

  • Go中的封装是通过命名约定和访问控制来实现的,而不像一些其他面向对象语言那样使用访问修饰符(如public、private、protected),因此开发者需要自觉遵守约定来保持封装的效果。
  • Go中通过结构体将相关的字段和方法组合在一起,并通过首字母大小写来控制其可访问性。结构体中的字段和方法使用大写字母开头表示公共的(可导出的),可以被其他包访问,而使用小写字母开头表示私有的(不可导出的),只能在当前包内使用。
  • Go中的封装更加宽泛,其封装的基本单元不是结构体而是包(package),包内的所有标识符(变量、函数、结构体、方法等)都通过首字母大小写来控制其可访问性

封装案例如下:

package modelimport "fmt"type Student struct {name   stringage    intgender string
}// 访问name字段
func (stu Student) GetName() string {return stu.name
}
func (stu *Student) SetName(name string) {stu.name = name
}// 访问age字段
func (stu Student) GetAge() int {return stu.age
}
func (stu *Student) SetAge(age int) {stu.age = age
}// 访问gender字段
func (stu Student) GetGender() string {return stu.gender
}
func (stu *Student) SetGender(gender string) {stu.gender = gender
}// Student的其他方法
func (stu Student) Print() {fmt.Printf("student info: <name: %s, age: %d, gender: %s>\n",stu.name, stu.age, stu.gender)
}

使用上述包内结构体的案例如下:

package mainimport ("go_code/OOP2/Encapsulate/model"
)func main() {// var stu = model.Student{"Alice", 12, "女"} // 隐式赋值var stu model.Studentstu.SetName("Alice")stu.SetAge(12)stu.SetGender("女")stu.Print() // student info: <name: Alice, age: 12, gender: 女>
}

注意: Go中无法对结构体中不可导出的字段进行隐式赋值,可以通过给结构体绑定对应的setter和getter方法对不可导出的字段进行访问和赋值。

工厂函数

工厂函数

如果结构体类型的首字母小写(不可导出),那么其他包将无法直接使用该结构体类型来创建实例,这时可以在包内提供对应可导出的工厂函数来创建实例并返回。如下:

package modelimport "fmt"type student struct {name   stringage    intgender string
}// 工厂函数
func NewStudent(name string, age int, gender string) *student {return &student{name:   name,age:    age,gender: gender,}
}func (stu student) Print() {fmt.Printf("student info: <name: %s, age: %d, gender: %s>\n",stu.name, stu.age, stu.gender)
}

其他包通过调用包中可导出的工厂函数,即可创建对应不可导出的结构体实例。如下:

package mainimport ("go_code/OOP2/Encapsulate/model"
)func main() {var stu = model.NewStudent("Alice", 12, "女")stu.Print() // student info: <name: Alice, age: 12, gender: 女>
}

继承

基本介绍

基本介绍

  • 继承是面向对象编程中的一个重要概念,其允许一个类(子类/派生类)继承另一个类(父类/基类)的属性和方法,子类继承父类后可以直接访问和使用父类中字段和方法,同时可以添加自己的字段和方法。
  • 继承的主要优势在于代码复用,继承可以在不重复编写相似代码的情况下扩展现有的类,使代码更具可维护性和可扩展性。

继承示意图如下:

在这里插入图片描述

继承的实现

继承的实现

  • Go中的继承是通过嵌套匿名结构体的方式来实现的,如果一个结构体中嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体中的字段和方法,从而实现了继承的效果。

继承案例如下:

package mainimport ("fmt"
)// 基类
type Person struct {Name stringAge  int
}
func (per Person) PrintInfo() {fmt.Printf("name = %s, age = %d\n", per.Name, per.Age)
}// 派生类
type Student struct {Person    // 嵌套匿名结构体StudentId int
}
func (stu Student) Study() {fmt.Printf("student %d is studying...\n", stu.StudentId)
}// 派生类
type Teacher struct {*Person   // 嵌套匿名结构体指针TeacherId int
}
func (tch Teacher) Teach() {fmt.Printf("teacher %d is teaching...\n", tch.TeacherId)
}func main() {var stu = Student{Person{"Alice", 12}, 100}stu.PrintInfo() // name = Alice, age = 12stu.Study()     // student 100 is studying...var tch = Teacher{&Person{"Bob", 22}, 200}tch.PrintInfo() // name = Bob, age = 22tch.Teach()     // teacher 200 is teaching...
}

说明一下:

  • 在嵌套匿名结构体时,可以通过Type的方式嵌套匿名结构体,也可以通过*Type的方式嵌套匿名结构体指针。
  • 在创建结构体变量时,如果要通过字段名的方式初始化结构体字段,那么匿名结构体的字段名由匿名结构体的类型名充当。
  • 在结构体中嵌套匿名结构体后,可以通过结构体实例访问匿名结构体的字段和方法,但在访问时仍然遵循Go的命名约定和访问控制。如果被嵌套的匿名结构体的定义在其他包,那么通过结构体实例只能访问匿名结构体可导出的字段和方法。
  • 结构体中嵌入的匿名字段也可以是基本数据类型,在访问结构体中的匿名基本数据类型字段时,以对应基本数据类型的类型名作为其字段名。比如结构体中嵌入了一个匿名int字段,则通过结构体变量名.int的方式对其进行访问。

组合

在结构体中嵌套有名结构体属于组合关系,在访问组合的结构体字段和方法时,必须带上结构体的字段名。如下:

package mainimport ("fmt"
)// 车轮
type Wheel struct {Color stringprice int
}// 自行车
type Bicycle struct {FrontWheel Wheel // 组合RearWheel  Wheel // 组合// ...
}
func (bc Bicycle) Print() {fmt.Printf("前轮<color:%s, price:%d元> 后轮<color:%s, price:%d元>\n",bc.FrontWheel.Color, bc.FrontWheel.price, bc.RearWheel.Color, bc.RearWheel.price)
}func main() {var bc = Bicycle{FrontWheel: Wheel{"black", 100},RearWheel:  Wheel{"blue", 200},}bc.Print() // 前轮<color:black, price:100元> 后轮<color:blue, price:200元>
}

字段和方法访问细节

字段和方法访问细节

结构体字段和方法的访问流程:

  1. 先查看当前结构体类型中是否有对应的字段或方法,如果有则访问。
  2. 再查看结构体中嵌入的匿名结构体中是否有对应的字段或方法,如果有则访问。
  3. 继续查找更深层次嵌入的匿名结构体中是否有对应的字段或方法,如果有则访问,否则产生报错。

案例如下:

package mainimport ("fmt"
)// 基类
type Person struct {Name stringAge  int
}
func (per Person) PrintInfo() {fmt.Printf("name = %s, age = %d\n", per.Name, per.Age)
}// 派生类
type Student struct {PersonStudentId int
}func (stu Student) Study() {fmt.Printf("student %d is studying...\n", stu.StudentId)
}
func (stu Student) PrintInfo() {fmt.Printf("name = %s, age = %d, id = %d\n", stu.Name, stu.Age, stu.StudentId)
}func main() {var stu = Student{Person{"Alice", 12}, 100}fmt.Printf("name = %s\n", stu.Name) // name = Alicestu.PrintInfo()                     // name = Alice, age = 12, id = 100
}

代码说明:

  • 在访问字段时,由于Student结构体中没有Name字段,因此Student变量.Name访问的是嵌套的匿名结构体Person中的Name字段。
  • 在访问方法时,由于Student结构体有PrintInfo方法,因此Student变量.PrintInfo()访问的是Student结构体的PrintInfo方法,而不是Person结构体的PrintInfo方法。

多继承

多继承

多继承指的是一个结构体中嵌套了多个匿名结构体,如果嵌套的多个匿名结构体中有相同的字段名或方法名,那么在访问时需要通过匿名结构体的类型名进行指明访问。如下:

package mainimport ("fmt"
)type Cat struct {Name stringAge  int
}type Dog struct {Name stringAge  int
}// 多继承
type Pet struct {CatDog
}func main() {var pet = Pet{Cat: Cat{Name: "小猫",Age:  2,},Dog: Dog{Name: "小狗",Age:  3,},}fmt.Printf("cat name = %s\n", pet.Cat.Name) // cat name = 小猫fmt.Printf("dog name = %s\n", pet.Dog.Name) // dog name = 小狗
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/696254.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

哪个牌子防水运动蓝牙耳机比较好?别错过四款高分防水耳机锦集!

在现代生活中&#xff0c;运动与音乐的交织早已超越单纯的休闲娱乐范畴&#xff0c;转而成为人们追求身心健康、提升运动表现的重要元素。尤其对于那些热爱户外运动、热衷于水上活动&#xff0c;或是追求无拘无束训练体验的健身爱好者而言&#xff0c;一款性能优越、防水可靠的…

【代码随想录】【动态规划】背包问题 - 完全背包

完全背包 模板&#xff1a;完全背包问题 问题描述 完全背包问题与01背包问题唯一的区别在于&#xff1a; 在01背包中&#xff1a;每个物品只有一个&#xff0c;要么放入背包&#xff0c;要么不放入背包在完全背包中&#xff1a;每个物品有无限多个&#xff0c;可以不放入背…

LSTM计算指示图

掌握网络结构组件构成 输入门、遗忘门、输出门候选记忆细胞记忆细胞隐藏状态ref&#xff1a;6.8. 长短期记忆&#xff08;LSTM&#xff09; — 《动手学深度学习》 文档 (gluon.ai)

内网环境安装使用DBeaver使用第一天

之前一直使用navicat&#xff0c;现在出于某种原因不让使用了&#xff0c;于是上手了这个工具&#xff0c;说实话&#xff0c;真的&#xff0c;但是必须要用。 首先安装的时候&#xff0c;必须要选择MySQL驱动&#xff0c;如果外网直接选择以后就可以下载了&#xff0c;内网需…

# 从浅入深 学习 SpringCloud 微服务架构(十五)

从浅入深 学习 SpringCloud 微服务架构&#xff08;十五&#xff09; 一、SpringCloudStream 的概述 在实际的企业开发中&#xff0c;消息中间件是至关重要的组件之一。消息中间件主要解决应用解耦&#xff0c;异步消息&#xff0c;流量削锋等问题&#xff0c;实现高性能&…

记录:robot_localization传感器数据融合学习

一、参考资料 官方&#xff1a; http://wiki.ros.org/robot_localizationhttp://docs.ros.org/en/noetic/api/robot_localization/html/index.html2015 ROSCon 演讲官方网址&#xff08;youyube上也有这个视频&#xff09; 实践教程 https://kapernikov.com/the-ros-robot_…

Shell的运行原理和Linux的权限

Shell的运行原理 Linux严格意义上说是一个操作系统&#xff0c;我们称之为“核心&#xff08;kernel&#xff09;”&#xff0c;但我们一般用户不能直接使用kernel&#xff0c;而是通过kernel的“外壳程序”&#xff0c;也就是所谓的Shell&#xff0c;来与kernel沟通。 Shell…

深入探究MySQL常用的存储引擎

前言 MySQL是一个广泛使用的开源关系型数据库管理系统&#xff0c;它支持多种存储引擎。存储引擎决定了MySQL数据库如何存储、检索和管理数据。不同的存储引擎具有不同的特点、性能表现和适用场景。选择适合的存储引擎对于优化数据库性能、确保数据完整性和安全性至关重要。本…

OSS证书自动续签,一分钟轻松搞定,解决阿里云SSL免费证书每3个月失效问题

文章目录 一、&#x1f525;httpsok-v1.11.0支持OSS证书自动部署介绍支持特点 二、废话不多说上教程&#xff1a;1、场景2、实战Stage 1&#xff1a;ssh登录阿里云 ECSStage 2&#xff1a;进入nginx &#xff08;docker&#xff09;容器Stage 3&#xff1a;执行如下指令Stage 3…

linux学习:多媒体开发库SDL+视频、音频、事件子系统+处理yuv视频源

目录 编译和移植 视频子系统 视频子系统产生图像的步骤 api 初始化 SDL 的相关子系统 使用指定的宽、高和色深来创建一个视窗 surface 使用 fmt 指定的格式创建一个像素点​编辑 将 dst 上的矩形 dstrect 填充为单色 color​编辑 将 src 快速叠加到 dst 上​编辑 更新…

代码随想录第五十一天|最长递增子序列、最长连续递增序列、最长重复子数组

题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09;

测评|喵都吃肥了,这篇主食冻干测评的推文终于完成了...VE、希喂、SC对比结果

想要为猫咪提供高质量的主食&#xff0c;主食冻干无疑是理想之选。主食冻干不仅肉含量高、易于吸收&#xff0c;而且富含多种普通猫粮难以提供的营养素&#xff0c;全面满足猫咪的微量元素需求。其营养价值与生骨肉喂养相媲美&#xff0c;同时避免了生骨肉可能带来的细菌超标问…