go【第四篇】面向对象

概述

和传统面向对象的区别

对于面向对象编程的支持Go 语言设计得非常简洁而优雅。
因为, Go语言并没有沿袭传统面向对象编程中的诸多概念,比如继承(不支持继承,尽管匿名字段的内存布局和行为类似继承,但它并不是继承)、虚函数、构造函数和析构函数、隐藏的this指针等。

面向对象三大特性实现方式

尽管Go语言中没有封装、继承、多态这些概念,但同样通过别的方式实现这些特性:

封装:通过方法实现
继承:通过匿名字段实现
多态:通过接口实现

对象创建

package main

import "fmt"

type Person struct {
    id   int
    name string
    age  int
}

func main() {
    var person = Person{101, "张三", 18}
    var person2 = Person{102, "李四", 19}

    fmt.Println(stu, stu2)
}

 

package main

import "fmt"

type Student struct {
    Person
    score float64
}


type Person struct {
    id   int
    name string
    age  int
}

func main() {
    //未全部初始化所有字段需要显式指出字段
    var stu = Student{Person{101, "张三", 18}, 90}
    var stu2 = Student{score:90}
    var stu3 = Student{Person:Person{101, "张三", 18}}
    var stu4 = Student{Person:Person{id:101, }}

    fmt.Println(stu)
    fmt.Println(stu2)
    fmt.Println(stu3)
    fmt.Println(stu4)
}


/*输出
{{101 张三 18} 90}
{{0  0} 90}
{{101 张三 18} 0}
{{101  0} 0}
*/
更多初始化示例

成员操作

package main

import "fmt"

type Student struct {
    Person
    id int
    score float64
}
type Person struct {
    id   int
    name string
    age  int
}

func main() {

    var stu = Student{Person{101, "张三", 18}, 90,88}
    var stu1 = Student{Person{102, "李四", 18}, 80, 99}
    fmt.Println(stu, stu1)
    stu.score = 91
    fmt.Println(stu, stu1)

    fmt.Println("张三考试成绩:", stu.score)
    fmt.Println("李四考试成绩:", stu1.score)
    //如果子对象字段和父对象重名
    fmt.Println("李四考试id:", stu1.Person.id)
    fmt.Println("李四考试id:", stu1.id)
}

/*输出
{{101 张三 18} 90 88} {{102 李四 18} 80 99}
{{101 张三 18} 90 91} {{102 李四 18} 80 99}
张三考试成绩: 91
李四考试成绩: 99
李四考试id: 102
李四考试id: 80
*/

指针类型匿名字段 

package main

import "fmt"

type Student struct {
	*Person // 匿名字段
	score float64
}
type Person struct {
	id   int
	name string
	age  int
}

func main()  {
	var stu Student=Student{&Person{101,"张三",18},90}
	fmt.Println(stu.name, stu.Person)


}
//输出
//张三 &{101 张三 18}

多重继承

package main

import "fmt"

type Student struct {
	Person
	score float64
}

type Person struct {
	Object
	name string
	age  int
}

type Object struct {
	id int
}

func main() {
	var stu Student
	stu.age = 18
	fmt.Println(stu.Person.age)
	stu.id = 101
	fmt.Println(stu.Person.Object.id)
	fmt.Println(stu.id)
}

/*
18
101
101
*/

基本方法创建  

package main

import "fmt"

type Int int // type 为int类型指定了别名.
func (a Int) TestInt(b Int) Int {
	return a + b
}

func main() {
	var num Int = 10
	var num1 Int = 20
	n := num.TestInt(20)
	n1 := num1.TestInt(5)
	fmt.Println(n)
	fmt.Println(n1)

}

为结构体添加方法

package main

import "fmt"

type Student struct {
	id   int
	name string
	age  int
}

// 接收者
func (s Student) PrintShow() {
	fmt.Println(s)
}

func (s *Student) EditInfo() { //以后一般采用传结构体的指针方式,因为可以影修改构体的内容
	s.age = 20
}

func main() {
	stu := Student{101, "张三", 18}
	stu.PrintShow() // 完成对方法的调用, 将stu中的值,传递给了方法的s.
	stu.EditInfo()
	stu.PrintShow()
}
package main

import "fmt"

type Student struct {
    id int
    name string
    age int
}

type Teacher struct {
    id int
    name string
}

func(s *Student) Show(){
    fmt.Println(s)
}

func(t *Teacher) Show(){
    fmt.Println(t)
}

func main() {
    // 如果接收者类型不同,即使方法的名字是相同的也是不同的方法。
  stu:=Student{101,"李四",18}
    //(&stu).Show()
    stu.Show()
  teacher:=Teacher{102,"老王"}
  teacher.Show()
}
使用方法注意事项  
package main

import "fmt"

//定义一个学生类,有六个属性,分别为姓名、性别、年龄、语文、数学、英语成绩
//第一方法:打招呼的方法:介绍自己叫XX,今年几岁了。是男同学还是女同学。
//第二个方法:计算总分与平均分的方法

// 1: 定义结构体
type StudentInfo struct {
    name    string
    sex     string
    age     int
    chinese float64
    math    float64
    english float64
}

// 2: 添加方法
func (s *StudentInfo) SayHello(userName string, userAge int, userSex string) {
    // 2.1 初始化
    s.name = userName
    s.age = userAge
    s.sex = userSex
    // 2.2  初始化后的值进行判断。
    if s.sex != "" && s.sex != "" {
        s.sex = ""
    }
    if s.age < 1 || s.age > 100 {
        s.age = 18
    }
    // 2.3 打印输出结果
    fmt.Printf("我叫%s,年龄是%d,性别是%s\n", s.name, s.age, s.sex)
}
func (s *StudentInfo) GetScore(chinese float64, math float64, english float64) {
    // 1: 初始化
    s.chinese = chinese
    s.math = math
    s.english = english
    // 2: 进行计算
    sum := s.chinese + s.math + s.english
    // 3: 打印输出结果
    fmt.Printf("我叫%s,总分%f,平均分%f", s.name, sum, sum/3)

}
func main() {
    var stu StudentInfo
    stu.SayHello("张三", 180, "df")
    stu.GetScore(90,89,87)
}
面向对象方法demo

  

 

封装

封装变量

结构体封装在变量中

封装函数

在Go语言中,可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法。

语法: func (receiver ReceiverType) funcName(parameters) (results)

只要接收者类型不一样,这个方法就算同名,也是不同方法,不会出现重复定义函数的错误

package main

import "fmt"

type MyInt int //自定义类型,给int改名为MyInt

//在函数定义时,在其名字之前放上一个变量,即是一个方法
func (a MyInt) Add(b MyInt) MyInt { //面向对象
    return a + b
}

//传统方式的定义
func Add(a, b MyInt) MyInt { //面向过程
    return a + b
}

func main() {
    var a MyInt = 1
    var b MyInt = 1

    //调用func (a MyInt) Add(b MyInt)
    fmt.Println("a.Add(b) = ", a.Add(b)) //a.Add(b) =  2

    //调用func Add(a, b MyInt)
    fmt.Println("Add(a, b) = ", Add(a, b)) //Add(a, b) =  2
}
基础类型绑定函数
package main

import "fmt"

type Person struct {
    name string //名字
    sex  byte   //性别, 字符类型
    age  int    //年龄
}

//带有接收者的函数叫方法
func (tmp Person) PrintInfo() {
    fmt.Println("tmp = ", tmp)
}

//通过一个函数,给成员赋值
func (p *Person) SetInfo(n string, s byte, a int) {
    p.name = n
    p.sex = s
    p.age = a
}

func main() {
    //定义同时初始化
    p := Person{"mike", 'm', 18}
    p.PrintInfo()

    //定义一个结构体变量
    var p2 Person
    (&p2).SetInfo("yoyo", 'f', 22)
    p2.PrintInfo()

}
结构体类型绑定函数

继承

继承变量

结构体中定义另一个结构体类型的变量

继承方法

如果匿名字段实现了一个方法,那么包含这个匿名字段的struct也能调用该方法。

package main

import "fmt"

type Person struct {
    name string //名字
    sex  byte   //性别, 字符类型
    age  int    //年龄
}

//Person类型,实现了一个方法
func (tmp *Person) PrintInfo() {
    fmt.Printf("name=%s, sex=%c, age=%d\n", tmp.name, tmp.sex, tmp.age)
}

//有个学生,继承Person字段,成员和方法都继承了
type Student struct {
    Person //匿名字段
    id     int
    addr   string
}

func main() {
    s := Student{Person{"mike", 'm', 18}, 666, "bj"}
    s.PrintInfo()
}
方法继承

重写

package main

import "fmt"

type Person struct {
    name string //名字
    sex  byte   //性别, 字符类型
    age  int    //年龄
}

//Person类型,实现了一个方法
func (tmp *Person) PrintInfo() {
    fmt.Printf("name=%s, sex=%c, age=%d\n", tmp.name, tmp.sex, tmp.age)
}

//有个学生,继承Person字段,成员和方法都继承了
type Student struct {
    Person //匿名字段
    id     int
    addr   string
}

//Student也实现了一个方法,这个方法和Person方法同名,这种方法叫重写
func (tmp *Student) PrintInfo() {
    fmt.Println("Student: tmp = ", tmp)
}

func main() {
    s := Student{Person{"mike", 'm', 18}, 666, "bj"}
    //就近原则:先找本作用域的方法,找不到再用继承的方法
    s.PrintInfo() //到底调用的是Person, 还是Student, 结论是Student

    //显式调用继承的方法
    s.Person.PrintInfo()
}
方法重写

方法值

package main

import "fmt"

type Person struct {
    name string //名字
    sex  byte   //性别, 字符类型
    age  int    //年龄
}

func (p Person) SetInfoValue() {
    fmt.Printf("SetInfoValue: %p, %v\n", &p, p)
}

func (p *Person) SetInfoPointer() {
    fmt.Printf("SetInfoPointer: %p, %v\n", p, p)
}

func main() {
    p := Person{"mike", 'm', 18}
    fmt.Printf("main: %p, %v\n", &p, p)

    p.SetInfoPointer() //传统调用方式

    //保存方式入口地址
    pFunc := p.SetInfoPointer //这个就是方法值,调用函数时,无需再传递接收者,隐藏了接收者
    pFunc()                   //等价于 p.SetInfoPointer()

    vFunc := p.SetInfoValue
    vFunc() //等价于 p.SetInfoValue()

}
View Code

方法表达式

换个传参形式

package main

import "fmt"

type Person struct {
    name string //名字
    sex  byte   //性别, 字符类型
    age  int    //年龄
}

func (p Person) SetInfoValue() {
    fmt.Printf("SetInfoValue: %p, %v\n", &p, p)
}

func (p *Person) SetInfoPointer() {
    fmt.Printf("SetInfoPointer: %p, %v\n", p, p)
}

func main() {
    p := Person{"mike", 'm', 18}
    fmt.Printf("main: %p, %v\n", &p, p)

    //方法值   f := p.SetInfoPointer //隐藏了接收者
    //方法表达式
    f := (*Person).SetInfoPointer
    f(&p) //显式把接收者传递过去 ====》 p.SetInfoPointer()

    f2 := (Person).SetInfoValue
    f2(p) //显式把接收者传递过去 ====》 p.SetInfoValue()
}
View Code

接口

接口定义

type Humaner interface {
    SayHi()
}

 

接⼝命名习惯以 er 结尾
接口只有方法声明,没有实现,没有数据字段
接口可以匿名嵌入其它接口,或嵌入到结构中
package main

import "fmt"

type Personer interface {
    SayHello()
    //Say()
}
type Student struct {

}
func (s *Student)SayHello(){
    fmt.Println("老师好")
}
type Teacher struct {

}

func (t *Teacher)SayHello()  {
    fmt.Println("学生好")
}

func main() {
    // 对象名.方法名
    var stu Student
    //stu.SayHello()
    //var teacher Teacher
    //teacher.SayHello()
    // 通过接口变量来调用,必须都实现接口中所声明的方法。


    var person Personer
    person=&stu
    person.SayHello() // 调用的是Student 实现的SayHello方法
    //person=&teacher
    //person.SayHello()

}
接口定义方式

 

package main

import "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(h Personer)  {
    h.SayHello()
}

func main() {
    var stu Student
    var teacher Teacher
    WhoSayHi(&stu)
    WhoSayHi(&teacher)
}
多态demo1
package main

import "fmt"

type Stroager interface {
    Read()
    Writer()
}

// 移动硬盘
type MDisk struct {
}

func (m *MDisk) Read() {
    fmt.Println("移动硬盘读取数据")
}
func (m *MDisk) Writer() {
    fmt.Println("移动硬盘写入数据")
}

// U盘
type UDisk struct {
}

func (u *UDisk) Read() {
    fmt.Println("U盘读取数据")
}
func (u *UDisk) Writer() {
    fmt.Println("U盘写入数据")
}
// 定义一个函数
func Computer(c Stroager){
    c.Read()
    c.Writer()
}
func main() {
  var udisk UDisk
  var mdisk MDisk
  Computer(&udisk)
  Computer(&mdisk)
}
多态demo2

 

接口实现

接口变量指向绑定好函数的结构体实例,指向后即可调用接口方法

package main

import "fmt"

//------------------  定义接口类型  ---------------------------------
type Humaner interface {
    //方法,只有声明,没有实现,由别的类型(自定义类型)实现
    sayhi()
}

// ------------------  封装Student Teacher ---------------------------------
type Student struct {
    name string
    id   int
}

//Student实现了此方法
func (tmp *Student) sayhi() {
    fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

type Teacher struct {
    addr  string
    group string
}

//Teacher实现了此方法
func (tmp *Teacher) sayhi() {
    fmt.Printf("Teacher[%s, %s] sayhi\n", tmp.addr, tmp.group)
}

func main() {
    s := &Student{"mike", 666}
    t := &Teacher{"bj", "go"}

    var i, j Humaner
    i, j = s, t
    i.sayhi()
    j.sayhi()

}
版本一
package main

import "fmt"

//------------------  定义接口类型  ---------------------------------
type Humaner interface {
    //方法,只有声明,没有实现,由别的类型(自定义类型)实现
    sayhi()
}

// ------------------  封装Student Teacher ---------------------------------
type Student struct {
    name string
    id   int
}

//Student实现了此方法
func (tmp *Student) sayhi() {
    fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

type Teacher struct {
    addr  string
    group string
}

//Teacher实现了此方法
func (tmp *Teacher) sayhi() {
    fmt.Printf("Teacher[%s, %s] sayhi\n", tmp.addr, tmp.group)
}

// ------------------ 方便接口使用 ---------------------------------
//只有一个函数,可以有不同表现,多态
func Fsayhi(i Humaner) {
    i.sayhi()
}

func main() {
    s := &Student{"mike", 666}
    t := &Teacher{"bj", "go"}

    //调用同一函数,不同表现,多态,多种形态
    Fsayhi(s)
    Fsayhi(t)

}
版本二(提高版本一易用性)
package main

import "fmt"

//------------------  定义接口类型  ---------------------------------
type Humaner interface {
    //方法,只有声明,没有实现,由别的类型(自定义类型)实现
    sayhi()
}

// ------------------  封装Student Teacher MyStr ---------------------------------
type Student struct {
    name string
    id   int
}

//Student实现了此方法
func (tmp *Student) sayhi() {
    fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

type Teacher struct {
    addr  string
    group string
}

//Teacher实现了此方法
func (tmp *Teacher) sayhi() {
    fmt.Printf("Teacher[%s, %s] sayhi\n", tmp.addr, tmp.group)
}

type MyStr string

//MyStr实现了此方法
func (tmp *MyStr) sayhi() {
    fmt.Printf("MyStr[%s] sayhi\n", *tmp)
}

// ------------------ 方便接口使用 ---------------------------------
//只有一个函数,可以有不同表现,多态
func Fsayhi(i Humaner) {
    i.sayhi()
}

func main() {
    s := &Student{"mike", 666}
    t := &Teacher{"bj", "go"}
    var str MyStr = "hello mike"

    //调用同一函数,不同表现,多态,多种形态
    Fsayhi(s)
    Fsayhi(t)
    Fsayhi(&str)

}
版本二+普通类型

接口继承 

package main

import "fmt"

type Humaner interface { //子集
    sayhi()
}

type Personer interface { //超集
    Humaner //匿名字段,继承了sayhi()
    sing(lrc string)
}

type Student struct {
    name string
    id   int
}

//Student实现了sayhi()
func (tmp *Student) sayhi() {
    fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}
//sing(lrc string)
func (tmp *Student) sing(lrc string) {
    fmt.Println("Student在唱着:", lrc)
}

func main() {
    //定义一个接口类型的变量
    var i Personer
    s := &Student{"mike", 666}
    i = s

    i.sayhi() //继承过来的方法
    i.sing("学生哥")
}
View Code

接口转换 

package main

import "fmt"

type Humaner interface {
    SayHello()
}
type Personer interface {
    Humaner
    Say()
}
type Student struct {
}

func (s *Student) SayHello() {
    fmt.Println("大家好")
}
func (s *Student) Say() {
    fmt.Println("你好")
}
func main() {
    var stu Student
    var per Personer
    per = &stu
    //per.Say()
    //per.SayHello() // 可以调用所继承的接口中的方法。

      var h Humaner
     h=per
     // per=h
      h.SayHello()


}
View Code
package main

import "fmt"

type Humaner interface { //子集
    sayhi()
}

type Personer interface { //超集
    Humaner //匿名字段,继承了sayhi()
    sing(lrc string)
}

type Student struct {
    name string
    id   int
}

//Student实现了sayhi()
func (tmp *Student) sayhi() {
    fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

func (tmp *Student) sing(lrc string) {
    fmt.Println("Student在唱着:", lrc)
}

func main() {
    //超集可以转换为子集,反过来不可以
    var iPro Personer //超集
    iPro = &Student{"mike", 666}

    var i Humaner //子集

    //iPro = i //err
    i = iPro //可以,超集可以转换为子集
    i.sayhi()

}
View Code

空接口

空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。它有点类似于C语言的void *类型。

var v1 interface{} = 1     // 将int类型赋值给interface{}
var v2 interface{} = "abc" // 将string类型赋值给interface{}
var v3 interface{} = &v2   // 将*interface{}类型赋值给interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}

断言

Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok = element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。

如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。
package main

import "fmt"

type Element interface{}

type Student struct {
    name string
    id   int
}

func main() {
    var i [3]Element
    //i := make([]Element, 3)
    i[0] = 1                    //int
    i[1] = "hello go"           //string
    i[2] = Student{"mike", 666} //Student

    //类型查询,类型断言
    //第一个返回下标,第二个返回下标对应的值, data分别是i[0], i[1], i[2]
    for index, data := range i {
        //第一个返回的是值,第二个返回判断结果的真假
        if value, ok := data.(int); ok == true {
            fmt.Printf("x[%d] 类型为int, 内容为%d\n", index, value)
        } else if value, ok := data.(string); ok == true {
            fmt.Printf("x[%d] 类型为string, 内容为%s\n", index, value)
        } else if value, ok := data.(Student); ok == true {
            fmt.Printf("x[%d] 类型为Student, 内容为name = %s, id = %d\n", index, value.name, value.id)
        }
    }

}
View Code

断言与空接口

 

package main

import "fmt"

// 加法类
type Add struct {
	Object
}

func (a *Add) GetResult() int { // 方法的实现要和接口中方法的声明保持一致
	return a.numA + a.numB
}
func (a *Add) SetData(data ...interface{}) bool {
	// 1: 对数据的个数进行校验。
	var b bool = true
	if len(data) > 2 {
		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
	}
	a.numA = value
	a.numB = value1
	// 2: 类型进行校验。
	return b
}

// 减法类
type Sub struct {
	Object
}

func (s *Sub) SetData(data ...interface{}) bool {
	// 1: 对数据的个数进行校验。
	var b bool = true
	if len(data) > 2 {
		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
	}
	s.numA = value
	s.numB = value1
	// 2: 类型进行校验。
	return b
}

func (s *Sub) GetResult() int {
	return s.numA - s.numB
}

type Object struct {
	numA int
	numB int
}
type Resulter interface {
	GetResult() int
	SetData(data ...interface{}) bool // 完成参数运算的数据的类型校验。
}

// 对象问题
// 1: 定义一个新的类
type OperatorFactory struct {
}

// 2: 创建一个方法,在该方法中完成对象的创建
func (o *OperatorFactory) CreateOperator(op string) Resulter {
	switch op {
	case "+":
		add := new(Add)
		return add
	case "-":
		sub := new(Sub)
		return sub

	default:
		return nil
	}
}
func OperatorWho(h Resulter) int {
	return h.GetResult()
}
func main() {
	var operator OperatorFactory
	obj:=operator.CreateOperator("-")
	b:=obj.SetData(30,10)
	if b {
		num:=OperatorWho(obj)
		fmt.Println(num)
	}

}

  

 

  

 

 

  

 

posted @ 2019-01-07 18:14  沐风先生  阅读(173)  评论(0编辑  收藏  举报