Go语言基础之面向对象编程中

1 Golang面向对象编程基本介绍

Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OPP语言不一样,随后分别介绍Golang对面向对象编程的三大特性是如何实现的。

2 面向对象编程-封装

2.1 封装介绍

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

2.2 封装的作用

用通俗的电视机来理解封装:电视机是由不同的零件组装在一起,具有将接收的信号显示在屏幕上功能;作为家电,其向用户展现的是对应的功能,用户不用考虑其内部是怎么实现,只要充分利用电视机所提供的功能即可。那么类比到程序中这样做有什么好处:

  • 隐藏实现细节;

  • 可以对数据进行验证,保证数据的安全。

2.3 Golang中的封装体现

  • 对结构体中的属性进行封装

  • 通过方法和包实现封装

2.4 封装实现步骤

Golang中封装实现步骤:

  • 将结构体、字段(属性)的首字母小写(其它包不能使用,类似private);

  • 给结构体所在包提供一个工厂模式的函数(构造函数),首字母大小;

  • 提供一个首字母大写的Set方法(类似其他语言的public),用于对属性判断并赋值 ,Set方法的结构如下:

func (var 结构体类型名) SetXxx(参数列表) (返回值列表) {
    //加入数据验证的业务逻辑
    var.字段 = 参数
}
  • 提供一个首字母大写的Get方法(类似其他语言的public),用于获取属性值,Get方法的结构如下:
func (var 结构体类型名) GetXxx(参数列表) (返回值列表) {
	return var.字段
}

另外,在Golang开发中并没有特别强调封装,Golang本身对面向对象的特性做了简化。

2.5 封装案例

需求:设计一个person结构体,不能随便查看person的年龄、工资等隐私,并对输入的年龄进行合理的验证。

设计:model包(person.go实现person结构体),main包(main.go调用peron结构体)。

代码实现:

//model/person.go
package model

//定义一个person的结构体
type person struct {
    Name string
    age int
    sala float64
}

//写一个工厂模式函数,类似构造函数
func NewPerson(name string) *person {
    return &person{
        Name : name
    }
}

//为了访问age 和 sala,以age字段为例编写对应的SetXxx和GetXxx方法
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
}

//main/main.go 导入model包
func main() {
    p := model.NewPerson("tom")
    p.SetAge(20)
    fmt.Println(p)
    fmt.Pritnln(p.Name, "age=", p.GetAge())
}

3 面向对象编程-继承

3.1 继承介绍

在现实中,一些事物有其共性,比如大学生、小学生都具有学生共有的属性和方法,那么我们可以将这些共同的属性和方法抽象出结构体并让大学生和小学生继承这些属性和方法,这样可以减少一些重复定义重复使用的工作。

继承可以提高代码复用、扩展性和维护性,让编程更加靠近人类思维。当多个结构体存在相同的属性和方式时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法。对于其他结构体不需要重新定义这些属性和方法,只需嵌套一个匿名结构体即可。以学生、大学生以及小学生为例,示意图如下:

在Golang中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性。

3.2 嵌套匿名结构体的基本语法

//以书籍继承货物为例
type Goods struct {
    Name string
    Price float64
}

type Book struct {
    Goods	//嵌套匿名结构体Goods
    Writer string
}

3.3 继承细节及注意事项

结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或小写的字段、方法都可以使用,示例如下:

type A struct {
	Name string 
   	age int
}

func (a *A) SayOk() {
    fmt.Println("A SayOk", a.Name)
}

func (a *A) hello() {
    fmt.Println("A hello", a.Name)
}

type B struct {
    A
}

func main() {
    var b B
    b.A.Name = "tom"
    b.A.SayOk()
    b.A.hello()
}

匿名结构体字段访问可以简化:

func main() {
    var b B
    b.A.Name = "tom"
    b.A.SayOk()
    b.A.hello()
    
    b.Name = "viktor"
    b.SayOk()
    b.hello()
}
/*
	当直接通过b访问字段或方法时,比如b.Name:
	编译器会先看b对应的类型有没有Name,如果有,则直接调用B类型的Name字段;
	如果没有就去找B中嵌套的匿名结构体A有没有声明Name字段,如果有就调用;
	没有的话继续查找,如果都找不到就报错
*/

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

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

type A struct {
    Name string
    age int
}

type B struct {
    Name string
    Score float64
}

type C struct {
    A
    B
}

func main() {
    var c C
    //c.Name = "tom"	//报错
    c.A.Name = "tom"
    fmt.Println(c)
}

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

type D struct {
    a A
}

func main() {
    var d D
    d.a.Name = "viktor"
}

嵌套匿名结构体后,也可以在创建结构体变量时,直接指定各个匿名结构体字段的值;

type Goods struct {
    Name string 
    Price float64
}

type Brand struct {
    Name string
    Address string
}

type TV struct {
    Goods
    Brand
}

type TV2 struct {
    *Goods
    *Brand
}

func main() {
    tv := TV{Goods{"001", 5000.88}, Brand{"长虹","四川"},}
    
    tv2 := TV{
        Goods{
            Price : 5000,
            Name : "002",
        },
        Brand{
            Name : "海尔"
            Address : "山东"
        },
    }
    
    fmt.Println("tv", tv)
    fmt.Println("tv2", tv2)
    
    tv3 := TV{&Goods{"003", 5000.88}, &Brand{"创维","河南"},}
    
    tv4 := TV{
        &Goods{
            Price : 5000,
            Name : "004",
        },
        &Brand{
            Name : "夏普"
            Address : "北京"
        },
    }
    
    fmt.Println("tv3", *tv3.Goods, *tv3.Brand)
    fmt.Println("tv4", *tv4.Goods, *tv4.Brand)   
}

3.4 多重继承

如果一个struct嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承。

在3.3中最后一个示例中,TV就是一个多重继承的结构体,继承了GoodsBrand两个结构体。

多重继承注意事项:

  • 如果嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来区分;
  • 为了保证代码的简洁性,建议尽量不要使用多重继承。

4练习

使用面向对象的思维方式编写一个学生信息管理系统:

  • 学生有id、姓名、年龄、分数等信息;
  • 程序提供展示学生列表、添加学生、编辑学生信息、删除学生等功能。
原文地址:https://www.cnblogs.com/dabric/p/12365294.html