文章

面向对象编程

OOP思想

面向过程的思维模式

面向过程的思维模式是简单的线性思维,思考问题首先陷入第一步做什么,第二部做什么的细节中。这种思维模式适合处理简单的事情,比如:上厕所

面向对象的思维模式

面向对象的思维模式说白了就是分类思维模式。首先思考解决问题,需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。

面向对象的三大特点:封装、继承、多态

继承

继承就是子类继承父类的特征和行为,使得子类具有父类的属性和方法,使得子类具有父类相同的行为。子类会具有父类的一般特性也会具有自身的特性。

package main

import "fmt"

/*
Go语言的结构体嵌套
1。模拟继承性: is-a
type A struct{
field
}
type B struct{
A
B可以直接访问A中的属性
}

2。模拟聚合关系 :has-a
type C struct{
field
}
type D struct{
c C
}
D就不能直接访问c当中的属性,而是需要通过D.c.xx 访问
*/

// 1。定义一个父"类"结构体
type Person struct {
   name string
   age  int
}

// 2。定义一个子"类"结构体
type Student struct {
   Person        // 匿名变量 继承
   school string //子类自己的书写字段
}

func main() {
   p1 := Person{name: "zuo", age: 18}
   fmt.Println(p1.name)

   s1 := Student{Person{name: "jiang", age: 20}, "sz"}
   fmt.Println(s1.Person.name)

   //提升字段
   //对于Student来说,Person是匿名字段,Person中的属性name、age就叫做提升字段
   //提升字段可以通过名字直接访问,不需要再同构结构体名Person
   fmt.Println(s1.name)
   var s3 Student
   s3.name = "zhao"
   s3.school = "北大"
   fmt.Println(s3)
}

image-20221222163933551

方法

方法

  • 某个类别的行为功能,需要指定的接受者调用
  • 一段独立的功能代码,可以直接调用

函数

  • 命名不能冲突
  • 一段独立的功能代码,可以直接调用
//函数所有人都可以调用
func funName(param list)(return list){}
//方法,多了一个接受者,并且只有这歌接受者类型才可以调用
func (t type) funName(param list)(return list){}
//接受者 可以是这个类型或者这个类型的指针
func (接受者) funName(param list)(return list){}
package main

import "fmt"

type Dog struct {
   name string
   age  int
}
type Cat struct {
   name string
   age  int
}

// 方法定义
func (dog Dog) eat() {
   fmt.Println("Dog,eating")
}
func (cat Cat) eat() {
   fmt.Println("Cat,eating")
}
func (dog Dog) sleep() {
   fmt.Println("dog,sleep")
}

func main() {
   dog := Dog{name: "jiang", age: 10}
   dog.eat()
   dog.sleep()

   cat := Cat{name: "喵喵", age: 5}
   cat.eat()
}

image-20221222171710879

方法重写

子类可以重写父类方法override

子类可以新增自己的属性和方法

子类可以直接访问父类的属性和方法

package main

import "fmt"

type Animal struct {
   name string
   age  int
}
type Dog struct {
   Animal
}
type Cat struct {
   Animal
   Color string
}

func (animal Animal) eat() {
   fmt.Println(animal.name, "正在吃")
}
func (animal Animal) sleep() {
   fmt.Println(animal.name, "正在睡觉")

}

// 子类重写父类的方法
func (cat Cat) eat() {
   fmt.Println(cat.name, "正在吃~~~~~~~")
}

// 子类扩展自己的方法
func (cat Cat) maio() {
   fmt.Println(cat.name, "miao~~")
}
func main() {
   dog := Dog{Animal{name: "旺财", age: 1}}
   dog.eat()

   cat := Cat{Animal{name: "喵喵", age: 2}, "blue"}
   cat.eat()
   cat.maio()
}

image-20221222174958761

接口实现

把所有的具有共性的方法定义在一起,任何其他类型只有实现接口定义的全部方法就是实现了这个接口

  • go语言不需要显示的实现接口
  • 实现接口中的方法,就算实现了接口
  • go语言中,接口和实现类的关系,是非侵入式的
package main

import "fmt"

type USB interface {
	oInput()
	oOutput()
}
type Mouse struct {
	name string
}

func (mouse Mouse) oInput() {
	fmt.Println(mouse.name, "鼠标输入")
}
func (mouse Mouse) oOutput() {
	fmt.Println(mouse.name, "鼠标输出")
}
func test(u USB) {
	u.oInput()
	u.oOutput()
}

type KeyBored struct {
	name string
}

func (k KeyBored) oInput() {
	fmt.Println(k.name, "键盘输入")
}
func (k KeyBored) oOutput() {
	fmt.Println(k.name, "键盘输出")
}

func main() {
	//通过传入接口实现类,来进行具体方法的调用
	m := Mouse{name: "罗技"}
	test(m)
	k := KeyBored{name: "雷蛇"}
	test(k)
	var usb USB
	usb = k
	fmt.Println(usb)
}

image-20221222200252776

多态

一个事务拥有多种形态。是面向对象中很重要的一个特点

go语言通过接口来模拟多态

package main

import "fmt"

type Animal interface {
   eat()
   sleep()
}
type Dog struct {
   name string
}

func (dog Dog) eat() {
   fmt.Println(dog.name, "eat")
}
func (dog Dog) sleep() {
   fmt.Println(dog.name, "sleep")
}
func test1(a Animal) {
   fmt.Println("test1")
   a.eat()
}
func main() {
   dog := Dog{name: "jiang"}
   dog.eat()
   dog.sleep()
   test1(dog)

   var animal Animal
   animal = dog
   fmt.Println(animal)
}

image-20221222233511663

空接口

不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值

package main

import "fmt"

type Dog struct {
   name string
}
type Cat struct {
   name  string
   color string
}

// 空接口
type A interface {
}

func test3(a A) {
   fmt.Println(a)
}
func test4(a interface{}) {
   fmt.Println(a)
}
func main() {
   var a1 A = Cat{
      name:  "毛毛",
      color: "蓝色",
   }
   var a2 A = Dog{
      name: "大黄",
   }
   var a3 A = "haha"
   var a4 A = 200
   fmt.Println(a1)
   fmt.Println(a2)
   fmt.Println(a3)
   fmt.Println(a4)
   test3(a1)
   test3(a2)
   test3(a3)
   test3(a4)
   fmt.Println("=========map==========")
   //map key string v obj
   map1 := make(map[string]interface{})
   map1["name"] = "zuo"
   map1["age"] = 20
   map1["dog"] = Dog{name: "小鲳鱼"}
   fmt.Println(map1)
   fmt.Println("=========切片=========")
   s1 := make([]interface{}, 0, 10)
   s1 = append(s1, a1, a3, a4, 200, "sada")
   fmt.Println(s1)
}

image-20221222235409771

接口嵌套

接口可以继承,还可以多继承

package main

import "fmt"

type A interface {
   test1()
}
type B interface {
   test2()
}
type C interface {
   A
   B
   test3()
}
type Dog struct {
}

func (dog Dog) test1() {
   fmt.Println("test1")
}
func (dog Dog) test2() {
   fmt.Println("test2")
}
func (dog Dog) test3() {
   fmt.Println("test3")
}
func main() {
   var dog Dog = Dog{}
   dog.test1()
   dog.test2()
   dog.test3()
   //在接口嵌套中,嵌套的接口默认继承了被嵌套接口的所有方法
   var ia A = dog
   ia.test1()
   var ib B = dog
   ib.test2()
   var ic C = dog
   ic.test1()
   ic.test2()
   ic.test3()
}

image-20221223000349868

接口断言

检查接口类型变量的值是否实现了期望的接口,就是检查当前接口类型的值有没有实现指定的接口。

把接口类型变量的值转换为其他类型或其他接口,go语言空interface()可以保存任何类型的变量,当程序中需要使用该变量的时候,需要把接口类型变量的值转化为具体类型,可以通过接口类型断言

假如接口类型保存的值是数字,当前的值要参与数学运算,就必须转为int类型才可以参加运算,这就是利用接口断言来实现类型转化的例子

被断言的对象必须是接口类型,否则会报错

t := i.(T)

func assertString(i interface{}) {
	s := i.(string)
	fmt.Println(s)
}
  • 断言成功,则t为T类型的接口值
  • 断言失败则报错,panic

v, ok := i.(T)

func assertInt(i interface{}) {
   s, ok := i.(int)
   if ok {
      fmt.Println("接口变量i是int类型")
      fmt.Println(s)
   } else {
      fmt.Println("接口变量i不是int类型")
   }
}

type-switch

func test5(i interface{}) {
   switch i.(type) {

   case string:
      fmt.Println("string类型")
   case int:
      fmt.Println("int类型")
   case I:
      fmt.Println("I类型")
   case nil:
      fmt.Println("nil类型")
   default:
      fmt.Println("未知类型")

   }
}

补充:type别名

package main

import "fmt"

// 通过type关键字的定义,DiyInt就是一种新的类型,它具有int的特性
type DiyInt int

func main() {
   var a DiyInt = 20
   var b int = 10
   c := int(a) + b
   fmt.Println(c)
   //定义int类型的别名为myInt
   //myInt类型只会在代码中存在,编译完成时并不会有myInt类型
   type myInt = int
   var q myInt = 30
   var d int = 40
   e := d + q
   fmt.Println(e)
}

image-20221223004601370

License:  CC BY 4.0 test