面向过程
所谓的面向过程就是:强调的是步骤、过程、每一步都是自己亲自去实现的。
面向对象
所谓的面向对象其实就是找一个专门做这个事的人来做,不用关心具体怎么实现的。
所以说,面向过程强调的是过程,步骤。而面向对象强调的是对象,也就是干事的人。
在程序中,可以通过属性和方法(函数)来描述类。属性就是特征,方法(函数)就是行为。
面向对象编程好处
封装
继承
多态
继承
继承是一种类间关系,描述一个类从另一个类获取成员信息的类间关系。
继承必定发生在两个类之间,参与继承关系的双方称为父类和子类。
父类提供成员信息,子类获取成员信息。
通过匿名字段来实现继承
package mainimport "fmt"type Student struct {//属性---成员//方法---函数Person //匿名字段,只有类型,没有成员的名字score float64
}type Teacher struct {Person //匿名字段,只有类型,没有成员的名字salary float64
}type Person struct {id intname stringage int
}func main() {var stu Student = Student{Person{1, "张三", 18}, 98}fmt.Println(stu) //{{1 张三 18} 98}//部分初始化var stu1 Student = Student{score: 90}fmt.Println(stu1) //{{0 0} 90}var stu2 Student = Student{Person: Person{id: 101}} //{{101 0} 0}fmt.Println(stu2)
}
成员操作
package mainimport "fmt"type Student struct {//属性---成员//方法---函数Person //匿名字段,只有类型,没有成员的名字score float64
}type Person struct {id intname stringage int
}func main() {var stu Student = Student{Person{101, "张三", 18}, 98}var stu1 Student = Student{Person{102, "李四", 18}, 80}//获取成员的值fmt.Println(stu.score) //98fmt.Println(stu1.score) //80fmt.Println(stu1.Person.id) //102 //比较麻烦fmt.Println(stu1.id) //102//修改成员的值stu.score = 100fmt.Println(stu.score) //100}
指针类型匿名字段
package mainimport "fmt"type Student struct {//属性---成员//方法---函数*Person //指针类型匿名字段score float64
}type Person struct {id intname stringage int
}func main() {var stu Student = Student{&Person{101, "张三", 18}, 98}fmt.Println(stu) //{0xc000054460 98} 父类输出的是结构体内存地址 只能使用成员操作了fmt.Println(stu.name) //张三
}
多重继承—尽量不要写多重继承
package mainimport "fmt"// Student 学生继承person
type Student struct {//属性---成员//方法---函数Person //指针类型匿名字段score float64
}// Person 父类 person继承object
type Person struct {Object1name stringage int
}type Object1 struct {id int
}func main() {var stu Studentstu.age = 18fmt.Println(stu.Person.age) //18stu.id = 101fmt.Println(stu.Person.Object1.id)
}
为结构体添加方法—封装
func (对象 结构体类型) 方法名 (参数列表)(返回值列表) {代码体}
方法调用
对象名.方法
//不支持重载,只要接收者类型不一样,这个方法就算同名,也是不同方法,不会出现重复定义函数的错误
package mainimport "fmt"// Student
type Student struct {id intname stringage int
}// PrintShow 方法 s为接收者
func (s Student) PrintShow() {fmt.Println(s) //{101 ziye 18}
}func (s Student) EditInfo() {s.age = 20
}func (s *Student) EditInfo1() {s.age = 20
}
func main() {stu := Student{101, "ziye", 18}//对象名.方法名 把stu中的值传给了sstu.PrintShow() //完成对方法的调用stu.EditInfo() //不是引用传递,是值传递stu.PrintShow()//内部会进行转换stu.EditInfo1() //引用传递stu.PrintShow() //{101 ziye 20}
}
注意事项
只要接收者类型不一样,这个方法就算同名,也是不同方法
接收者为指针类型
package mainimport "fmt"// Student
type Student struct {id intname stringage int
}type Teacher struct {id intname string
}func (s *Student) show() {fmt.Println(s)
}func (t *Teacher) show() { //把teacher内存地址给到这里fmt.Println(t)
}
func main() {//如果接收者类型不同,即使方法的名字是相同的也是不同的方法stu := Student{101, "ziye", 18}stu.show() //等价于(&stu).show()teacher := Teacher{102, "ziyeye"}teacher.show()
}
面向对象方法练习
定义一个学生类,有六个属性,分别为姓名、性别、年龄、语文、数学、英语成绩
定义两个方法:
第一方法:打招呼的方法:介绍自己叫XX,今年几岁了。是男同学还是女同学。
第二个方法:计算总分与平均分的方法
package mainimport "fmt"// Student
type StudentInfo struct {name string //姓名sex string //性别age int //年龄chinese float64 //语文math float64 //数学english float64 //英语
}// SayHello 打招呼
func (studentInfo *StudentInfo) SayHello(username string, userAge int, userSex string) {//初始化studentInfo.name = usernamestudentInfo.age = userAgestudentInfo.sex = userSex//初始化后的值进行判断if studentInfo.sex != "男" && studentInfo.sex != "女" {studentInfo.sex = "男"}if studentInfo.age < 1 || studentInfo.age > 100 {studentInfo.age = 18}//打印输出结果fmt.Printf("我叫%s,年龄是%d,性别是%s\n", studentInfo.name, studentInfo.age, studentInfo.sex)
}// GetScore 计算平均分
func (studentInfo *StudentInfo) GetScore(chinese float64, math float64, english float64) {//初始化studentInfo.chinese = chinesestudentInfo.math = mathstudentInfo.english = english//进行计算sum := studentInfo.chinese + studentInfo.math + studentInfo.english//打印输出结果fmt.Printf("我叫%s,总分%f,平均分%.2f\n", studentInfo.name, sum, sum/3)
}
func main() {var stu StudentInfostu.SayHello("ziye", 18, "女")stu.GetScore(98, 97, 95)
}
方法继承
package mainimport "fmt"// Student
type Student struct {Personscore float64
}type Person struct {id intname string //姓名age int //年龄
}func (p *Person) PrintInfo() {fmt.Println(*p) //{101 张三 18}
}func main() {stu := Student{Person{101, "张三", 18}, 90}//子类可以调用父类的方法stu.PrintInfo()
}
方法继承的练习
根据以下信息,实现对应的继承关系
记者:我叫张三 ,我的爱好是偷拍,我的年龄是34,我是一个男狗仔。
程序员:我叫孙全,我的年龄是23,我是男生,我的工作年限是 3年。
package mainimport "fmt"// Person 定义父类
type Person struct {name stringage intsex string
}// SetValue 给父类添加方法
func (p *Person) SetValue(userName string, userAge int, userSex string) {p.name = userNamep.age = userAgep.sex = userSex
}// Rep 定义相应的子类 记者类
type Rep struct {PersonHobby string //爱好
}// Pro 程序员类
type Pro struct {PersonWorkYear int
}// RepSayHello 给子类添加相应的信息
func (r *Rep) RepSayHello(Hobby string) {r.Hobby = Hobbyfmt.Printf("我叫%s ,我的爱好是%s,我的年龄是%d,我是一个%s狗仔\n", r.name, r.Hobby, r.age, r.sex)
}// ProSayHello 给子类添加相应的信息
func (p *Pro) ProSayHello(workYear int) {p.WorkYear = workYearfmt.Printf("我叫%s,我的年龄是%d,我是%s,我的工作年限是 %d年\n", p.name, p.age, p.sex, p.WorkYear)
}
func main() {var rep Reprep.SetValue("ziye", 34, "男")rep.RepSayHello("偷拍")var pro Propro.SetValue("李四", 26, "男")pro.ProSayHello(3)}
方法重写
就是子类(结构体)中的方法,将父类中的相同名称的方法的功能重新给改写了
注意:在调用时,默认调用的是子类中的方法
package mainimport "fmt"// Person 定义父类
type Person struct {name stringage int
}func (p *Person) PrintInfo() {fmt.Println("这是父类中的方法")
}type Student struct {Personscore float64
}func (s *Student) PrintInfo() {fmt.Println("这是子类中的方法")
}func main() {var stu Studentstu.PrintInfo() //这是子类中的方法 如果父类中的方法名称与子类中的方法一致,那么通过子类的对象调用的是子类中的方法,方法重写stu.Person.PrintInfo() //这是父类中的方法 调用父类中的方法
}
方法值与方法表达式
package mainimport "fmt"// Person 定义父类
type Person struct {name stringage int
}func (p *Person) PrintInfo() {fmt.Println(*p) //{ziye 18}
}func main() {per := Person{"ziye", 18}per.PrintInfo()//方法值f := per.PrintInfofmt.Printf("%T\n", f) //func() 方法类型f() //{ziye 18}//方法表达式f1 := (*Person).PrintInfo //并没有指定一个对象 类名要和方法接收者类型保存一致f1(&per) //{ziye 18} 方法表达式
}
接口简介
接口就是一种规范与标准,只是规定了要做哪些事情。具体怎么做,接口是不管的。
接口把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实
现了这个接口。
接口定义
type 接口名字 interface {
方法声明
}
接口的声明
可以为结构体添加接口中的方法,完成接口中方法实现
package mainimport "fmt"// Personer 接口的声明
type Personer interface {SayHello() //方法声明
}type Student struct {
}// SayHello 使用student完成SayHello
func (s *Student) SayHello() {fmt.Println("老师好")
}type Teacher struct {
}func (t *Teacher) SayHello() {fmt.Println("学生好")
}func main() {//对象名.方法名var stu Studentstu.SayHello() //老师好var teacher Teacherteacher.SayHello() //学生好//接口变量来调用,必须都实现接口中所声明的方法var person Personerperson = &stuperson.SayHello() //调用的是Student、实现的SayHello方法 老师好person = &teacherperson.SayHello() //学生好}
多态的定义与实现
什么是多态
所谓多态:指的是多种表现形式。
多态就是同一个接口,使用不同的实例而执行不同操作
package mainimport "fmt"type Personer interface {SayHello()
}type Student struct {
}func (s *Student) SayHello() {fmt.Println("老师好")
}type Teacher struct {
}func (t *Teacher) SayHello() {fmt.Println("学生好")
}//实现多态
func WhoSayHi(personer Personer) {personer.SayHello()
}func main() {var stu Studentvar teacher TeacherWhoSayHi(&stu)WhoSayHi(&teacher)
}
案例
用多态来模拟实现 将移动硬盘或者U盘插到电脑上进行读写数据
package mainimport "fmt"type Stroager interface {Read()Writer()
}// MDisk 移动硬盘
type MDisk struct {
}func (m *MDisk) Read() {fmt.Println("移动硬盘读取数据")
}func (m *MDisk) Writer() {fmt.Println("移动硬盘写入数据")
}// UDisk U盘
type UDisk struct {
}func (u *UDisk) Read() {fmt.Println("U盘读取数据")
}func (u *UDisk) Writer() {fmt.Println("U盘写入数据")
}// Computer 定义一个函数
func Computer(s Stroager) {s.Writer()s.Read()
}
func main() {var uds UDiskvar mds MDiskComputer(&uds)Computer(&mds)
}
案例
使用面向对象方式,实现一个计算器程序
案例实现一
package mainimport "fmt"type Object1 struct {
}func (o *Object1) GetResult(num1, num2 int, op string) int {//添加参数var result intswitch op {case "+":result = num1 + num2case "-":result = num1 - num2}return result
}func main() {var obj Object1result := obj.GetResult(8, 6, "+")fmt.Println(result)
}
案例实现二
package mainimport "fmt"// 加法类
type Add struct {Object1
}func (add *Add) GetResult() int { //方法的实现要和接口中方法的声明保持一致return add.numA + add.numB
}type Sub struct {Object1
}func (sub *Sub) GetResult() int {return sub.numA - sub.numB
}type Object1 struct {numA intnumB int
}type Resulter interface {GetResult() int //返回类型int
}func main() {add := Add{Object1{10, 8}}result := add.GetResult()fmt.Println(result)sub := Sub{Object1{10, 8}}result = sub.GetResult()fmt.Println(result)
}
案例实现三
package mainimport "fmt"// 加法类
type Add struct {Object1
}func (add *Add) GetResult() int { //方法的实现要和接口中方法的声明保持一致return add.numA + add.numB
}type Sub struct {Object1
}func (sub *Sub) GetResult() int {return sub.numA - sub.numB
}type Object1 struct {numA intnumB int
}type Resulter interface {GetResult() int //返回类型int
}// OperatorFactory 对象创建问题
// 1:定义一个新的类
type OperatorFactory struct {
}// CreaeteOperator 创建一个方法,在该方法中完成对象的创建----封装
func (o *OperatorFactory) CreaeteOperator(op string, numA, numB int) int {switch op {case "+":add := Add{Object1{numA, numB}}return OperatorWho(&add)case "-":sub := Sub{Object1{numA, numB}}return OperatorWho(&sub)default:return 0}
}
// 多态
func OperatorWho(h Resulter) int {result := h.GetResult()return result
}
func main() {var operator OperatorFactorycreaeteOperator := operator.CreaeteOperator("+", 20, 10)fmt.Println(creaeteOperator)
}
接口的继承与转换
package mainimport "fmt"type Humaner interface {SayHello()
}// Personer 接口继承
type Personer interface {HumanerSay()
}
type Student struct {
}func (s *Student) SayHello() {fmt.Println("大家好")
}func (s *Student) Say() {fmt.Println("你好")
}func main() {var Stu Studentvar per Personerper = &Stuper.Say()per.SayHello() //可以调用所继承的接口中的方法//接口转换var h Humanerh = perh.SayHello()//超集可以转换为子集,反过来不可以//per = h
}
空接口定义与使用
var i interface{} //空接口
i = 123
fmt.Println(i)
空接口可以赋任意的类型,切片s可以赋值各种类型的
var s []interface{} //空接口切片ss = append(s, 123, "abc", 23.12)for i := 0; i < len(s); i++ {fmt.Println(s[i])}
空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值
类型断言
通过类型断言,可以判断空接口中存储的数据类型。
语法:value, ok := m.(T)
m:表空接口类型变量
T:是断言的类型
value: 变量m中的值。
ok: 布尔类型变量,如果断言成功为true,否则为false
package mainimport "fmt"func main() {var i interface{}i = 123value, ok := i.(int)if ok {fmt.Println(value)} else {fmt.Println("类型推断错误")}
}
案例–空接口与类型断言综合应用
计算器,完成数据校验
package mainimport "fmt"// 加法类
type Add struct {Object1
}func (add *Add) GetResult() int { //方法的实现要和接口中方法的声明保持一致return add.numA + add.numB
}func (add *Add) SetData(data ...interface{}) bool {//对数据的个数进行校验var b bool = trueif len(data) > 2 || len(data) <= 1 {fmt.Println("参数个数错误")b = false}value, ok := data[0].(int)if !ok {fmt.Println("第一个数类型错误")b = false}value1, ok1 := data[1].(int)if !ok1 {fmt.Println("第二个数类型错误")b = false}add.numA = valueadd.numB = value1//对传递过来的类型进行校验return b
}type Sub struct {Object1
}func (sub *Sub) GetResult() int {return sub.numA - sub.numB
}
func (sub *Sub) SetData(data ...interface{}) bool {//对数据的个数进行校验var b bool = trueif len(data) > 2 || len(data) <= 1 {fmt.Println("参数个数错误")b = false}value, ok := data[0].(int)if !ok {fmt.Println("第一个数类型错误")b = false}value1, ok1 := data[1].(int)if !ok1 {fmt.Println("第二个数类型错误")b = false}sub.numA = valuesub.numB = value1//对传递过来的类型进行校验return b
}type Object1 struct {numA intnumB int
}type Resulter interface {GetResult() int //返回类型intSetData(data ...interface{}) bool //完成参数运算的数据的类型校验
}// OperatorFactory 对象创建问题
// 1:定义一个新的类
type OperatorFactory struct {
}// CreaeteOperator 创建一个方法,在该方法中完成对象的创建----封装
func (o *OperatorFactory) CreaeteOperator(op string) Resulter {switch op {case "+":add := new(Add) //返回*addreturn addcase "-":sub := new(Sub) //创建Sub 开辟相应的存储空间return subdefault:return nil}
}// 多态
func OperatorWho(h Resulter) int {result := h.GetResult()return result
}
func main() {var operator OperatorFactorycreaeteOperator := operator.CreaeteOperator("-") //拿到对象setData := creaeteOperator.SetData(30, 10)if setData {who := OperatorWho(creaeteOperator)fmt.Println(who)}}