【golang基础】9.模拟oop

方法是撒子?

方法就是一个包含了接受者的函数(即指定了能够使用该方法的对象),接受者可以是命名类型或者结构体类型的一个值或一个指针。所有给定类型的方法属于该类型的方法集。

方法只是一个函数,它带有一个特殊的接收器类型,它是在func关键字和方法名之间编写的,接收器可以是struct类型或非struct类型,接收方可以在方法内部访问。

方法的定义

func (t Type)methodName(...)(...){

//

}

type Worker struct {
    name string
    age  int
}
func (w Worker) work() {
    fmt.Println(w.name, " is working")
}//方法可以用值或者指针调用,当调用者是指针时其实是(*w).name,
    //不过go语言中可以将*省略,于是就都是w.name
func main() {
    w1 := Worker{"axuezm", 20}
    w1.work()
    fmt.Println(w1)
​
    w2 := new(Worker)
    w2.name = "xuezma"
    w2.age = 22
    fmt.Println(w2)
    w2.work()
}

tips:方法名可以相同,但同名方法的接受者不能一样

接口是撒子?

面向对象世界中的接口一般定义是“接口定义对象的行为”,它表示指定对象应该做什么,实现这种行为的方法是针对对象的。

在go语言中,接口是一组方法签名,当类型为(为:四声)接口中的所有方法提供定义时,它被称为实现接口。接口指定了类型应该具有的方法,类型决定了如何实现这些方法。

接口将所有具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口,如果某个对象实现了某个接口的所有方法,则此对象就实现了该接口。

设计接口的目的在于降低程序的耦合性,即各部分程序之间的关联性尽量降低,这样修改一部分代码时就不会牵扯到另外的代码。

go语言中,接口和类型的实现关系是非侵入式的。

接口的定义

type inferface_name interface{

method_name1()

method_name2()

...

}

//例子:定义一个USB接口,并创建鼠标以及U盘类实现了该接口
type USB interface {
    start()
    end()
}
type Mouse struct {
    name string
}
type UPan struct {
    name string
}
​
func (m Mouse) start() {
    fmt.Println(m.name, "start working")
}
func (m Mouse) end() {
    fmt.Println(m.name, "end working")
}
​
func (u UPan) start() {
    fmt.Println(u.name, "start working")
}
func (u UPan) end() {
    fmt.Println(u.name, "end working")
}
​
func printMes(usb USB) {//当需要接口类型的对象时,可以使用任意实现类对象代替
    usb.start()
    usb.end()
}
func main() {
    m1 := Mouse{"shubiao1"}
    printMes(m1)
    u1 := UPan{"Upan1"}
    printMes(u1)
    //var usb USB
    //usb = m1
    //usb.name()//接口对象不能访问实现类中的属性
}
​

空接口

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

例如:

type A interface{
}
type Cat struct{
    name string
}
type Person{
    name string
}
func main(){
    var c1 A = Cat{"tonny"}
    var p1 A = Person{"axuezm"}
    var n1 A = "HELLOWORLD"
    var n2 A = 100
    fmt.Println(c1)
    fmt.Println(p1)
}

空接口的应用1:可以将某些函数的参数设置为空接口,即代表该函数可以接受任意类型的数据作为参数

func testfunc(a interface{}){//直接上匿名接口即可
    fmt.Println(a)
}

空接口的应用2:可以将某些容器存储的类型设置为空接口,这样该容器就能存储任意的类型了

m := make(map[string]interface{})//创建一个map,键是string类型,值为任意类型
m["name"] = "axuezm"
m["age"] = 20
m["friend"] = Person{"xuezma",21}

接口嵌套

一个类可以实现多个接口,但在声明的时候需要表示创建的是哪一个接口的实现

type A interface {
    test1()
}
type B interface {
    test2()
}
type C interface {
    A
    B
    test3()
}
type Cat struct {
}
​
func (cat Cat) test1() {
    fmt.Println("test1")
}
func (cat Cat) test2() {
    fmt.Println("test2")
}
func (cat Cat) test3() {
    fmt.Println("test3")
}
func main() {
    var cat Cat = Cat{}
    var a1 A = cat
    a1.test1()
    var b1 B = cat
    b1.test2()
    var c1 C = cat
    c1.test1()
    c1.test2()
    c1.test3()
}
​

接口断言

方式一:

1.instance := 接口对象.(实际类型) //不安全,断言错误会panic()恐慌

2.instance ,ok := 接口对象.(实际类型) //安全

方式二:switch

switch instance := 接口对象.(type){

case 实际类型1:

...

case 实际类型2:

...

...

}

接口断言是用来判断某个接口是否是某个具体的实现,如:

有一个接口,它有两个方法是求面积和周长,有一个三角形类和一个圆形类实现了这个接口。当你创建了一个接口对象并使用了三角形类的对象对它进行赋值时,它实际上还是一个接口类,不能调用三角形类中的属性和方法,此时使用接口断言可以判断该接口对象是不是三角形类。

package main
import "fmt"
type animal interface {
    shout()
    getColor() string
}
​
type Cat struct {
    color string
    foot  int
}
type Dog struct {
    color string
    foot  int
}
​
func (c Cat) shout() {
    fmt.Println("喵喵喵")
}
func (c Cat) getColor() string {
    return c.color
}
func (d Dog) shout() {
    fmt.Println("汪汪汪")
}
func (d Dog) getColor() string {
    return d.color
}
func getType(a animal) {
    /*************************方法一***************************/
    if ins, ok := a.(Cat); ok {
        ins.shout()
        fmt.Printf("我是一只喵,我是%s,我有%d只脚\n", ins.getColor(), ins.foot)
    } else if ins, ok := a.(Dog); ok {
        ins.shout()
        fmt.Printf("我是一只汪,我是%s,我有%d只脚\n", ins.getColor(), ins.foot)
    } else {
        fmt.Println("俺也不知道俺是个啥")
    }
}
func getType2(a animal) {
    /***************************方法二**************************/
    switch ins := a.(type) {
    case Cat:
        ins.shout()
        fmt.Printf("我是一只喵,我是%s,我有%d只脚\n", ins.getColor(), ins.foot)
    case Dog:
        ins.shout()
        fmt.Printf("我是一只汪,我是%s,我有%d只脚\n", ins.getColor(), ins.foot)
    default:
        fmt.Println("俺也不知道俺是个啥")
    }
}
func printMes(a animal) {
    a.shout()
    fmt.Println("我是", a.getColor())
}
func main() {
    var cat Cat = Cat{"白色的", 4}
    printMes(cat)
    var dog Dog = Dog{"五彩斑斓的", 3}
    printMes(dog)
    var a animal = cat
    printMes(a)
​
    getType(cat)
    getType(dog)
    getType(a)
    
    var a1 animal
    getType(a1)
    getType2(a1)
    getType(cat)
}

注:参数是接口类型的时候参数可以是接口类型的实现,可以是值传递也可以是指针传递,要注意值传递时会拷贝原数据,指针传递时会拷贝一个指针(指针指向的位置不会变,即一样会改变原数据)

go语言使用结构体模拟OOP

go语言不是面向对象的语言,但我们可以通过结构体来模拟面向对象中的类以及继承

1.继承

模拟继承需要前文所讲的结构体嵌套以及匿名字段,在子类中增加一个字段为父类类型

type Person struct{
    name string
    sex string
}
type Student struct{
    Person
    school string
    score int
}

2.提升字段

在结构体中属于匿名结构体的字段称为提升字段,因为它们可以被访问,就好像它们属于拥有匿名结构体字段的结构一样。

如在上面的例子中,Student结构体中的Person字段为匿名字段,同时它也是提升字段。

我们可以直接访问提升字段中的字段而不需要通过提升字段(有点拗口昂),如:

s1 := Student{}
s1.name = "axuezm"
s1.age = 18
s1.school = "nicai"
s1.score = 100

以上例子中,本来name这个字段需要通过s1.Person.name来访问,但作为提升字段我们直接使用name就可以访问它了,而不需要通过提升字段Person

type student2 struct {
	p Person
	score int
}//这样不是匿名字段的就不属于提升字段,不能直接通过student2的对象访问,必须要先访问p再访问p中的字段==》s2.p.name

3.子类可以直接调用父类的方法

func (p Person)eat(){
	fmt.Println("父类:吃")
}
s1.eat()

子类可以重写父类的方法

func (p Student)eat(){
	fmt.Println("子类:吃")
}

4. 使用接口来模拟多态

对一个接口的实现来说:

看成实现本身的类型的话,它能够访问实现类中的属性和方法

看成是对应的接口类型的话,那就只能访问接口中的方法

一个函数如果接受接口类型作为参数,那么实际上可以传入该接口的任意实现类对象作为参数

定义一个类型为接口类型,实际上可以赋值为任意实现类的对象(但此时它不能访问实现类的属性和方法)

你可能感兴趣的:(golang,golang,开发语言,后端)