700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 07 面向对象编程-结构 封装 继承 多态 接口

07 面向对象编程-结构 封装 继承 多态 接口

时间:2021-09-01 19:57:07

相关推荐

07 面向对象编程-结构 封装 继承 多态 接口

文章目录

Struct定义struct 方法工厂模式面向对象三大特性封装继承接口多态Golang支持面向对象编程,但是和传统面向对象有区别,并不是纯粹面向对象的语言,只能说Golang支持面向对象编程特性Golang没有类,通过结构体struct 来实现OOP,非常简洁。

Struct定义

Struct字段声明语法同变量,创建一个结构体后如果没有为字段赋值 则字段取值为默认值结构体可以进行type 重新定义,Golang默认是新类型结构体是用户单独定义的类型,和其他类型转换时需要有完全相同的字段(名字、个数、类型)struct 每个字段 都可以写上一个tag,该tag 可以通过反射机制获取结构体所有字段在内存中是连续的

示例代码

//声明一个结构体type Cat struct{Name stringAge intColor stringHobby string}func test01(){//创建结构体并赋值1var cat1 Catcat1.Name = "小白"cat1.Age = 10cat1.Color = "白色"cat1.Hobby = "吃 鱼"var cat2 Catfmt.Println(cat1,cat2)//{小白 10 白色 吃 鱼} { 0 }//创建结构体并赋值2c3 := Cat{"小黑",20,"黑色","shuijiao"}//创建结构体并赋值3var c4 *Cat = new(Cat)(*c4).Name = "小花" //通过索引访问值 再访问属性c4.Age = 10 //也可以直接通过索引访问属性(goLang做了优化)//创建结构体并赋值4var c5 *Cat = &Cat{}c5.Name = "c5"fmt.Println(cat1,cat2,c3,c4,c5)c6 := c3c6.Name ="c6"//赋值后会直接克隆一个变量给C6,修改c6的值 c3 不受影响//{小黑 20 黑色 shuijiao} {c6 20 黑色 shuijiao}fmt.Println(c3,c6)//创建结构体变量时直接指定字段值var c7 = Cat{Name:"小绿",Age:40,}fmt.Println(c7)}

type Point struct{x inty int}type Rect struct{leftUp, rightDown Point}type Rect2 struct{leftUp, rightDown *Point}func test02(){//结构体所有字段在内存中是连续的r1 := Rect{Point{1,2} , Point{3,4}}fmt.Printf("r1.rd.x地址= %p r1.rd.y地址= %p r1.lu.x地址= %p r1.lu.y地址= %p \n",&r1.rightDown.x,&r1.rightDown.y,&r1.leftUp.x,&r1.leftUp.y)//fmt.Printf("r1.rd.x地址= %p r1.rd.y地址= %p r1.lu.x地址= %p r1.lu.y地址= %p \n",//&r1.rightDown.x,&r1.rightDown.y,&r1.leftUp.x,&r1.leftUp.y)r2 := Rect2{&Point{1,2} , &Point{3,4}}fmt.Printf("r2.rd.x地址= %p r2.rd.y地址= %p r2.lu.x地址= %p r2.lu.y地址= %p \n",&r2.rightDown.x,&r2.rightDown.y,&r2.leftUp.x,&r2.leftUp.y)//结构体是用户单独定义的类型,和其他类型转换时需要有完全相同的字段(名字、个数、类型)type A struct{Num int}type B struct{Num int}var a Avar b Ba = A(b)a.Num =10fmt.Println(a,b)//结构体进行type 重新定义,Golang默认是新类型,type AA Avar aa AA//var a A = aa 报错需要强转var a3 A = A(aa)fmt.Println(aa,a3)//struct 每个字段 都可以写上一个tag,该tag 可以通过反射机制获取//常见的场景就是 序列化和反序列化type Monster struct {Name string `json:"name"`Age int `json:age`}m1 := Monster{"牛魔王",300}jsonstr ,err := json.marshal(m1)if err != nil {fmt.Println("json 字符串处理错误")}fmt.Println("json 字符串=",jsonstr)}

struct 方法

struct 方法声明语法 表示 A结构体有一个test 方法 (a A)标识这个方式是和 A类型绑定的

func( a A)test(){} func( a A)test2(参数列表)(返回值列表){方法体return 返回值}

注意点

在通过一个结构变量去调用方法时,调用机制和函数相同,只不过调用结构体方法时,结构体变量也会作为一个参数传递到方法中。Golang中的方法是指定在数据类型上的,因此所有自定义类型都可以有方法,包括 struct、int 、float32如果一个类型实现了 String() 这个方法,那么 fmt.Println 默认会调用变量的这个方法作为结果进行输出通过变量去调用方法时,其(参数传递)机制和函数一样,不一样的地方是变量本身也会作为参数传递给该方法(如果变量是值类型则进行值拷贝,如果是引用类型则进行地质拷贝,如果希望在方法中修改结构体的值可以使用指针来传递)。方法的访问范围控制和函数一样,方法名首字母小写只在本包访问,大写可以在包外访问。

示例代码

//定义结构体type Person struct{Name string}//定义方法func (p Person) speak(){fmt.Println(p.Name,"是一个好人")}func test03(){p := Person{"zhangsan"}p.speak()}

工厂模式

工厂模式用来通过指定的方法来创建(只在包内可用的/首字母小写的结构体)实例。

同时如果结构体的属性 首字母小写也可以通过方法类获取(类似于java中的set/get 方法)

实例代码

func test04(){var stu = mode.NewStudent("tom",12.0)fmt.Println(*stu)fmt.Println(stu.Name,stu.GetScore())}package modeltype student struct{Name stringscore float64}//通过工厂方法获取 隐藏结构的实例func NewStudent(n string,s float64) *student{return &student{Name:n,score:s}}//通过方法或去被封装的属性func( stu *student) GetScore () float64{return stu.score}

面向对象三大特性

Golang也有面向对象编程的三大特性,只不过实现方式和其他OOP语言不同

封装

将抽象出来的字段封装在一起

好处:隐藏实现细节 同时借助统一访问方法,可以对数据进行验证,保证数据合理性

实现方式

将结构体、字段首字母小写(类似java private)为封装的结构体提供工厂模式函数,首字母大写,类似一个构造函数为结构体提供首字母大写的Set/Get 方法 用于属性读写

示例代码

func test05(){var per = mode.NewPerson("TOM")per.SetAge(50)per.SetSal(10000.0)fmt.Println(per)fmt.Println(per.GetAge(),per.GetSal(),per.Name)}package modelimport ("fmt")type person struct{Name stringage intsal 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)SetSal(sal float64){if sal>3000 && sal<30000{p.sal = sal } else{fmt.Println("薪资不在合法范围内")}}func (p *person)GetAge() int {fmt.Println("====>",p.age)return p.age}func (p *person)GetSal() float64 {fmt.Println("====>",p.sal)return p.sal}

继承

继承可以解决代码的复用,当多个结构体存在相同的字段和方法时,可以抽象出一个结构体包含公有的属性和方法。其他结构体不用定义这些属性和方法,只需要嵌套这个结构体即可。

就是说如果一个结构体嵌套了另外一个匿名结构体,那么这个结构体可以直接访问匿名结构体的方法和字段,如此就实现了继承。

基本语法形式

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

示例代码

func test06(){pu := &Pupil{}pu.Student.Name = "TOM"pu.Student.Age =12pu.Age=13//对继承的字段可以简化访问pu.testing();pu.Student.ShowInfo()pu.ShowInfo()//方法简化访问//声明结构体时直接为嵌套结构体赋值pu2 := &Pupil{Student{Name: "Jack",Age:19,Score:100,},}pu2.ShowInfo()}//定义学生类type Student struct{Name stringAge intScore int}//显示学生信息func (stu *Student)ShowInfo(){fmt.Println(stu)}func (stu *Student) SetCore(score int){stu.Score = score}//定义小学生类type Pupil struct{Student//嵌入 学生结构体}func (p *Pupil) testing(){fmt.Println("小学生正在考试...")}

注意点

结构体可以使用匿名结构体的所有字段和方法,无论首字母大写还是小写结构体访问匿名结构体方法和属性可以简化,简化访问时访问逻辑:如果当前结构体有访问属性/方法则直接调用本结构体的该属性/方法,如果当前结构体没有该属性/方法,则到嵌套的匿名结构体中找该属性/方法,找到就调用,如果找不到就报错。当结构体和嵌套结构体有相同的属性或方法时,编译器采用就近访问原则。如果想调用匿名结构体中的属性/方法这个时候就必须 通过匿名结构体的名字进行调用(不能进行简化调用)如果一个结构体嵌套了多个结构体,且多个嵌套结构体包含相同的属性/方法,那么在调用时就必须指明匿名结构体的名字。否则编译器报错如果一个结构体嵌套了一个有名字的结构体,那么二者是组合关系,这个时候访问组合结构体的属性/方法时必须带上 结构体的名字嵌套结构体变量后,可以在创建结构体变量时直接为嵌套结构体赋值。如果一个结构体中嵌套了基本数据类型如int,访问方式 A.int=12, 如果要有多个int字段必须制定名字

多重继承

如果一个struct 嵌套了多个匿名结构体,那么它可以访问所有结构体的字段和方法,这就是Golang的多重继承

*如果嵌套的多个结构体中包含相同的属性/方法,那么在调用方法时必须明确嵌套结构体的名字

接口

interface 类型可以定义一组方法,但是不需要实现,并且interface 不能包含任何变量,在具体类型中根据实际情况实现这些方法。

type 接口名 interface{method1()method2()}

示例程序

type Gun interface{Fire()}type Gun51 struct{}func(g Gun51) Fire(){fmt.Println("Gun51连续发射7mm子弹...")}type GunRPG struct{}func(g GunRPG) Fire(){fmt.Println("RPC发射火箭弹...")}func test07(){var g51 Gun51var rpg GunRPGvar gun Gun = g51gun.Fire();gun = rpggun.Fire()}

注意点

接口里所有的方法都没有方法体,体现了程序 高内聚低耦合的思想Golang 中不显式的实现接口,只要一个变量中包含接口声明的所有方法,就认为这个变量实现了这个接口。接口本身不创建实例,但是可以指向一个实现了该接口的实例一个自定义类型只有实现了某个接口,才可以将该类型变量赋值给这个接口。任何自定义类型都可以实现接口 不一定是结构体,且一个变量可以实现多个接口接口A可以继承多个其他接口B、C,这个时候如果变量实现A接口也必须同时实现B和C接口。如果接口(包括继承的接口)中包含重复的方法,那么编译器报错interface 默认是一个指针类型,如果没有初始化默认输出nil空接口 没有任何方法,所有类型都实现了空接口,即所有类型都可以赋值给空接口

继承和接口比较

继承主要解决代码的复用性和可维护性。强调共性复用。

接口主要是设计规范,让其他类型遵守规范 。强调共性能力 各自实现。

接口一定程度上实现代码解耦

多态

变量具有多种形态,Golang中多态通过接口实现。按照同一个接口在不同变量中做不同实现,这时接口就呈现多种不同状态。(见接口示例)

一般通过方法参数体现多态

类型断言

如何将一个接口变量赋值给自定义类型变量?需要先确定变量的类型,即类型断言

示例代码

func test08(){var x interface{}var b2 int = 1x = b2// 将x 转换为int y, ok := x.(int); if ok {fmt.Printf("转换成功 y的类型是%T 值是=%v \n",y,y);}else{fmt.Println("转换失败")}fmt.Println("x.(int)==>",x.(int))//fmt.Println("x.(type)==>",x.(type)) 只能在switch 中使用 这里使用报错//调用断言方法TypeJudge(b2)}func TypeJudge(items... interface{}){for index,x := range items{switch x.(type){case bool :fmt.Printf("第%v个参数是 bool 类型,值是 %v \n",index,x)case float32:fmt.Printf("第%v个参数是 float32 类型,值是 %v \n",index,x)case float64:fmt.Printf("第%v个参数是 float64 类型,值是 %v \n",index,x)case int,int32,int64:fmt.Printf("第%v个参数是 int 类型,值是 %v \n",index,x)case string:fmt.Printf("第%v个参数是 string 类型,值是 %v \n",index,x)default:fmt.Printf("第%v个参数 类型不确定,值是 %v \n",index,x)}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。