Go面向对象——抽象封装继承

抽象

抽象?博主不玩抽象谢谢》》》

搞错了,再来

在OOP编程中,抽象是一个很重要的思想,就是就是把现实世界中的复杂事物,用简洁、清晰的模型表达出来,隐藏不必要的实现细节,只暴露必要的接口。

虽然但是,只是提一嘴,大家自行体会

封装

封装就是把抽象出来的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作,才能对字段进行操作。

封装的好处在于可以隐藏实现细节,可以对数据进行验证,保证安全合理。

我们可以通过,结构体中的属性或者方法、包进行封装。

实现的步骤

1.将结构体、字段首字母小写,不能导出了其他包不能使用

2.给结构体所在包提供一个工厂模式函数,首字母大写,类似一个构造函数

3.提供一个首字母大写的Set方法,用于对属性判断并赋值

 func (var 结构体类型名)Setxxx(参数列表)(返回值){
  加入逻辑
 }

4.提供一个首字母大写的Get方法,用于获取属性的值

 func (var 结构体类型名)Setxxx(参数列表)(返回值){
  return var.字段
 }

Golang开发中并没有特别强调封装,并不是像Java一样,Golang的面向对象的特性做了简化

快速入门

需求:我们需要写一个程序,不能随便查看人的年龄,工资,并且对输入的年龄做一个合理性验证

model/person.go

 package model
 ​
 import "fmt"
 ​
 type person struct {
  Name string
  age  int
  sal  float64
 }
 ​
 func NewPerson(name string) *person {
  return &person{
  Name: name,
  }
 }
 func (p *person) SetAge(age int) {
  if age > 0 && age < 150 {
  p.age = age
  } else {
  fmt.Println("年龄范围不正确")
  }
 }
 func (p *person) Getage() int {
  return p.age
 }
 func (p *person) SetSal(sal float64) {
  if sal > 0 && sal < 1000000 {
  p.sal = sal
  } else {
  fmt.Println("薪资范围不正确")
  }
 }
 func (p *person) GetSal() float64 {
  return p.sal
 }
 ​

main/main.go

 package main
 ​
 import (
  "fmt"
  "go_code/project01/pointerdemo/encapsulationDemo/model"
 )
 ​
 func main() {
  p := model.NewPerson("smith")
  p.SetAge(18)
  p.SetSal(10000)
  fmt.Println(p)
 }
 ​

小练习

创建程序,在model包中定义Account结构体:在main函数中体会Golang的封装性。

  1. Account结构体要求具有字段:账号(长度在6-10之间)、余额(必须>20)、密码(必须是六位)
  2. 通过SetXxx的方法给Account 的字段赋值。(同学们自己完成)
  3. 在main函数中测试

model/person.go

 package model
 ​
 import "fmt"
 ​
 type information struct {
  account  string
  balance  float64
  password string
 }
 func NewInformation() *information {
  return &information{}
 }
 func (a *information) SetAccount(acc string) {
  if len(acc) > 6 && len(acc) < 10 {
  a.account = acc
  } else {
  fmt.Println("输入不合规请重新输入")
  }
 }
 ​
 func (a *information) SetBalance(bal float64) {
  if bal > 20 {
  a.balance = bal
  } else {
  fmt.Println("输入不合规请重新输入")
  }
 }
 ​
 func (a *information) SetPassword(pwd string) {
  if len(pwd) == 6 {
  a.password = pwd
  } else {
  fmt.Println("输入不合规请重新输入")
  }
 }
 func (a *information) GetAccount() string {
  return a.account
 }
 func (a *information) GetBalance() float64 {
  return a.balance
 }
 func (a *information) GetPassword() string {
  return a.password
 }
 ​

main/main.go

 package main
 ​
 import (
  "fmt"
  "go_code/project01/pointerdemo/encapsulationDemo/model"
 )
 ​
 func main() {
  a := model.NewInformation()
  a.SetAccount("ttdr_top")
  a.SetBalance(10000.00)
  a.SetPassword("123456")
  fmt.Printf("账号:%v,密码:%v,余额:%v", a.GetAccount(), a.GetPassword(), a.GetBalance())
 }
 ​
image-20251112170938019

继承

我们依旧以三角洲为例,所有干员都可以进行相同的动作,或者说有相同的字段,比如说:奔跑,下蹲,静步,但也有不同的字段,比如说:露娜的电箭,乌鲁鲁的巡飞弹,如果我们要一个一个定义所有干员,我们会发现会冗余很多很多相同的代码,比如说奔跑这个代码,你就需要写n遍,这样不利于维护,也不利于功能拓展,这时候我们就可以用继承来解决这个问题。

基本介绍

继承可以解决代码复用,让我们的编程更加靠近人类思维。

当多个结构体存在相同的属性和方法的时候,可以从这些结构体中抽象出结构体,在该结构体中定义相同的属性和方法。

Golang的继承是通过在结构体内增加匿名结构体来实现的,也就是说Golang中如果一个结构体嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法。

基本语法

 type Operator struct {
  Name string
  Type string
 }
 type Luna struct {
  Operator //嵌入了一个匿名结构体
  Grenades int
 }

快速入门

 package main
 ​
 import "fmt"
 ​
 type Operator struct {
  Name string
  Type string
  HP   int
 }
 type Luna struct {
  Operator
  Grenades    int
 }
 func (l *Luna) ThrowGrenade() {
  if l.Grenades > 0 {
  l.Grenades--
  fmt.Printf("[%s] 投掷破片手雷!剩余: %d\n", l.Name, l.Grenades)
  } else {
  fmt.Printf("[%s] 手雷已耗尽!\n", l.Name)
  }
 }
 ​
 func main() {
  player := &Luna{
  Operator: Operator{
  Name: "Luna",
  Type: "Reconnaissance",
  HP:   100,
  },
  Grenades:    1,
  }
  fmt.Printf("干员:%s | 类型:%s | 生命值:%d\n", player.Name, player.Type, player.HP)
  player.ThrowGrenade()
  player.ThrowGrenade()
 }
 ​
image-20251112205812531

在没有重复字段的情况下,Operator的字段会被提升到 Luna 中,所以可以直接访问 Name, Type, HP

继承的深入讨论

1.结构体可以使用嵌套匿名结构体所有的字段和方法,无论首字母是大写还是小写

2.当结构体和匿名结构体有相同字段或者方法时,编译器采用就近访问原则,如果希望访问匿名结构体的字段或者方法,可以通过匿名结构体名来区分。

3.结构体嵌入了两个或者多个匿名结构体,两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段或者方法),在访问时必须明确指定匿名结构体的名字,否则会编译报错

4.如果一个结构体嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或者方法的时候必须带上结构体的名字。

5.嵌套匿名结构体后,可以在创建匿名结构体变量的时候,直接给匿名结构体的字段赋值

6.结构体内可以嵌入匿名结构体也可以嵌入基本数据类型比如int,但是不能再有第二个int的匿名字段

多重继承

说白了就是在一个结构体嵌套了多个结构体

比如说,老黑和露娜都能丢手雷,那我就把手雷独立出来作为一个结构体

 package main
 ​
 import "fmt"
 ​
 type Operator struct {
  Name string
  Type string
  HP   int
 }
 type Grenadier struct {
  Grenades int
 }
 type Luna struct {
  Operator
  Grenadier
 }
 ​
 func (l *Luna) ThrowGrenade() {
  if l.Grenades > 0 {
  l.Grenades--
  fmt.Printf("[%s] 投掷破片手雷!剩余: %d\n", l.Name, l.Grenades)
  } else {
  fmt.Printf("[%s] 手雷已耗尽!\n", l.Name)
  }
 }
 ​
 func main() {
  player := &Luna{
  Operator: Operator{
  Name: "Luna",
  Type: "Reconnaissance",
  HP:   100,
  },
  Grenadier: Grenadier{Grenades: 1},
  }
  fmt.Printf("干员:%s | 类型:%s | 生命值:%d\n", player.Name, player.Type, player.HP)
  player.ThrowGrenade()
  player.ThrowGrenade()
 }
 ​

Luna结构体中有两个匿名结构体。

为了代码的简洁性,以及方便维护,尽量不要用多重继承

暂无评论

发送评论 编辑评论


				
上一篇
下一篇