12 面向对象
[TOC]
面向对象 OOP
面向对象三大特性
封装、继承、多态
Go面向对象编程特性
Go没有class类,Go使用struct替代了类的作用。
Go支持面向对象编程(OOP),但是和传统的面向对象有区别,并不是纯粹的面向对象语言,因此Go定义为支持面向对象编程是比较准确的。
Go没有类,Go的结构体和面向对象语言的class有同等地位,Go通过struct来实现OOP特性。
Go面向对象编程非常简洁,去掉了传统OOP语言的继承、方法重载、构造函数、析构函数、隐藏的this指针等等。
Go有面向对象编程的继承、封装和多态的特性,只是实现的方式和其它OOP语言不一样,如继承,Go没有extends关键字,继承是通过匿名字段来实现。
Go的OOP很优雅,OOP本身就是语言类型系统的一部分,通过接口Interface关联,耦合性低,非常灵活,因此Go是面向接口编程,面向接口编程是它非常重要的特性。
封装
工厂模式
Go中没有构造函数,通过工厂模式替代构造函数功能。当结构体定义为私有,但是别的包又需要用到它,就可以使用工厂模式实现。
私有结构体实例化:写一个可以生成私有结构体实例并将实例返回的公有函数,外界通过调用函数获得实例。
私有字段访问:为结构体绑定可操作私有字段的方法,如GET方法读取私有字段,SET方法修改私有字段。
封装
将一类事物共有的属性和行为提取出(抽象),形成一个模板(struct)。
将字段和对字段的操作封装在一起,数据保护在内部,其它包只能通过暴露的方法操作字段。
隐藏实现细节,对数据进行验证,保证数据安全。
go实现封装的方式
将结构体和它的字段首字母小写,以达到私有的目的。
为结构体提供一个工厂模式的公开函数,用于外界获取结构体实例,类似于构造函数。
为需要暴露的结构体字段绑定一套Get、Set方法,Get方法可以获取字段数据,Set方法修改字段数据,Set方法中可以对数据合理性进行判断。
go不强调封装,go本身对面向对象的特性做了简化,因为go认为程序作者有义务保证数据合理性。
继承(组合)
Go没有继承的语义,结构和字段之间是“has a”的关系,而不是“is a”的关系。没有父子的概念,仅仅是整体和局部的概念,所以一般称这种嵌套的结构体和字段的关系为组合。
组合的优点
代码冗余减少,有利于维护、功能扩展。
go使用嵌套匿名结构体的方式实现组合
结构体嵌套匿名结构体所有的字段和方法都可使用,包括私有的。
嵌套匿名结构体继承中,编辑器采用就近访问原则,实例本身拥有的字段优先于匿名结构体中的字段。
嵌套匿名结构体和结构体本身有一样的字段的时候,其实是两个字段都存在,一个叫b.Name,另一个是b.A.Name
可以通过匿名结构体名称来访问匿名结构体本身的变量,如b.A.Name,它和b.Name是完全独立的两个变量
嵌入多个结构体,它们之间有相同字段,在宿主没有它的字段的情况下,必须指定是哪个结构体的才能正确访问。
嵌套匿名结构体实例化
嵌套匿名结构体实例化也可以直接指定各个匿名结构体的字段的值
多重组合
嵌套多个匿名结构体
该结构体可以直接访问嵌套的结构体的字段和方法,从而实现多重组合
!为了保证代码间接性,一般不使用多重组合
嵌入基本数据类型
结构体也可以匿名嵌入基本数据类型
type A struct {int} //匿名嵌入int,使用时a.int = 9999的方式使用
组合关系
有名结构体嵌入称为组合关系,访问有名结构体字段必须带上结构体名
组合方法集
方法集调用,直接通过类型实例调用方法时Go编译器会进行自动转换(实例或指针转换为方法绑定的指针或实例)。
Go函数的调用实参都是值拷贝,方法调用参数传递也是一样的机制,具体类型变量传递给接口时也是值拷贝,如果传递给接口变量的值类型,但调用方法的接受者是指针类型,则程序运行时虽然能够将接受者转换为指针,但这个指针是指针的副本,并不是原变量的指针。Go在编译时做了严格的方法集合的检查,不允许产生这种调用,如果传递给接口的变量是指针类型则接口调用的是值类型的方法,程序运行时能够自动转换为值类型。
多态
实例具有多种形态即为多态。
在Go中多态特征是通过接口实现的,可以按照统一的接口来调用不同的实现,这时接口变量就呈现不同的形态。
类型断言 assert
判断实例是否是指向一个类型的变量。
目标类型变量 = 实例.(目标类型)
带检测的类型断言,即使类型不匹配也不会报panic
目标类型变量,成功与否 := 实例.(目标类型)
多态与类型断言应用
猫和狗都实现了动物接口,共有sleep()方法,而有一个work(a animal)函数,猫和狗的实例都可调用,只需要1个函数形参为animl接口,这样猫和狗都可以使用这个函数,在函数中通过类型断言来确定猫和狗的不同工作。
Last updated