面向对象编程思想-抽象

抽象的介绍

我们在前面去定义一个结构体时候,实际上就是把一类事物的共有的 属性( 字段)和 行为( 方法)提取

出来,形成一个 物理模型(结构体)。这种研究问题的方法称为抽象

比如一个银行账户:

package main

import (
"fmt"
)
//定义一个结构体Account
type Account struct {
AccountNo string
Pwd string
Balance float64
} //方法
//1. 存款
func (account *Account) Deposite(money float64, pwd string) { //看下输入的密码是否正确
if pwd != account.Pwd {
fmt.Println("你输入的密码不正确")
return
} //看看存款金额是否正确
if money <= 0 {
fmt.Println("你输入的金额不正确")
return
} account.Balance += money
fmt.Println("存款成功~~") } //取款
func (account *Account) WithDraw(money float64, pwd string) { //看下输入的密码是否正确
if pwd != account.Pwd {
fmt.Println("你输入的密码不正确")
return
} //看看取款金额是否正确
if money <= 0 || money > account.Balance {
fmt.Println("你输入的金额不正确")
return
} account.Balance -= money
fmt.Println("取款成功~~") } //查询余额
func (account *Account) Query(pwd string) { //看下输入的密码是否正确
if pwd != account.Pwd {
fmt.Println("你输入的密码不正确")
return
} fmt.Printf("你的账号为=%v 余额=%v \n", account.AccountNo, account.Balance) } func main() { //测试一把
account := Account{
AccountNo : "gs1111111",
Pwd : "666666",
Balance : 100.0,
} //这里可以做的更加灵活,就是让用户通过控制台来输入命令...
//菜单....
account.Query("666666")
account.Deposite(200.0, "666666")
account.Query("666666")
account.WithDraw(150.0, "666666")
account.Query("666666") }

面向对象编程三大特性-封装

基本介绍

Golang 仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它 OOP 语言不一

样,下面我们一一为同学们进行详细的讲解 Golang 的三大特性是如何实现的。

封装介绍

封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其

它包只有通过被授权的操作(方法),才能对字段进行操作

封装的理解和好处

  1. 隐藏实现细节
  2. 提高对 数据进行验证,保证安全合理(Age)

如何体现封装

  1. 对结构体中的属性进行封装
  2. 通过 方法,包 包 实现封装

封装的实现步骤

  1. 将结构体、字段(属性)的首字母小写(不能导出了,其它包不能使用,类似 private)
  2. 给结构体所在包提供一个工厂模式的函数,首字母大写。类似一个构造函数
  3. 提供一个首字母大写的 Set 方法(类似其它语言的 public),用于对属性判断并赋值

    func (var 结构体类型名) SetXxx(参数列表) (返回值列表) {

    //加入数据验证的业务逻辑

    var.字段 = 参数

    }
  4. 提供一个首字母大写的 Get 方法(类似其它语言的 public),用于获取属性的值

    func (var 结构体类型名) GetXxx() {

    return var.age;

    }

    特别说明:在 Golang 开发中并没有特别强调封装,这点并不像 Java. 所以提醒学过 java 的朋友,

    不用总是用 java 的语法特性来看待 Golang, Golang 本身对面向对象的特性做了简化的.

看一个案例

请大家看一个程序(person.go),不能随便查看 人的年龄, 工资等隐私,并对输入的年龄进行合理的验

证。设计: model 包(person.go) main 包(main.go 调用 Person 结构体)

main.go

package main
import (
"fmt"
"go_code/code/chapter11/encapsulate/model"
) func main() { p := model.NewPerson("smith")
p.SetAge(18)
p.SetSal(5000)
fmt.Println(p)
fmt.Println(p.Name, " age =", p.GetAge(), " sal = ", p.GetSal()) }

moudel

package model
import "fmt" type person struct {
Name string
age int //其它包不能直接访问..
sal float64
} //写一个工厂模式的函数,相当于构造函数
func NewPerson(name string) *person {
return &person{
Name : name,
}
} //为了访问age 和 sal 我们编写一对SetXxx的方法和GetXxx的方法
func (p *person) SetAge(age int) {
if age >0 && age <150 {
p.age = age
} else {
fmt.Println("年龄范围不正确..")
//给程序员给一个默认值
}
} func (p *person) GetAge() int {
return p.age
} func (p *person) SetSal(sal float64) {
if sal >= 3000 && sal <= 30000 {
p.sal = sal
} else {
fmt.Println("薪水范围不正确..") }
} func (p *person) GetSal() float64 {
return p.sal
}

面向对象编程三大特性-继承

看一个问题,引出继承的必要性

一个小问题,看个学生考试系统的程序 extends01.go,提出代码复用的问题

  1. Pupil 和 Graduate 两个结构体的字段和方法几乎,但是我们却写了相同的代码, 代码复用性不

  2. 出现代码冗余,而且代码 不利于维护,同时 也不利于功能的扩展。
  3. 解决方法-通过 继承方式来解决
package main

import (
"fmt"
) //编写一个学生考试系统 type Student struct {
Name string
Age int
Score int
} //将Pupil 和 Graduate 共有的方法也绑定到 *Student
func (stu *Student) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 成绩=%v\n", stu.Name, stu.Age, stu.Score)
}
func (stu *Student) SetScore(score int) {
//业务判断
stu.Score = score
} //给 *Student 增加一个方法,那么 Pupil 和 Graduate都可以使用该方法
func (stu *Student) GetSum(n1 int, n2 int) int {
return n1 + n2
} //小学生
type Pupil struct {
Student //嵌入了Student匿名结构体
} //显示他的成绩 //这时Pupil结构体特有的方法,保留
func (p *Pupil) testing() {
fmt.Println("小学生正在考试中.....")
} //大学生, 研究生。。 //大学生
type Graduate struct {
Student //嵌入了Student匿名结构体
} //显示他的成绩
//这时Graduate结构体特有的方法,保留
func (p *Graduate) testing() {
fmt.Println("大学生正在考试中.....")
} //代码冗余.. 高中生.... func main() { //当我们对结构体嵌入了匿名结构体使用方法会发生变化
pupil := &Pupil{}
pupil.Student.Name = "tom~"
pupil.Student.Age = 8
pupil.testing()
pupil.Student.SetScore(70)
pupil.Student.ShowInfo()
fmt.Println("res=", pupil.Student.GetSum(1, 2)) graduate := &Graduate{}
graduate.Student.Name = "mary~"
graduate.Student.Age = 28
graduate.testing()
graduate.Student.SetScore(90)
graduate.Student.ShowInfo()
fmt.Println("res=", graduate.Student.GetSum(10, 20))
}

继承可以解决代码复用,让我们的编程更加靠近人类思维。

当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体(比如刚才的

Student),在该结构体中定义这些相同的属性和方法。

其它的结构体不需要重新定义这些属性(字段)和方法,只需嵌套一个 Student 匿名结构体即可

也就是说:在 Golang 中,如果一个 struct 嵌套了另一个匿名结构体,那么这个结构体可以直接访

问匿名结构体的字段和方法,从而实现了继承特性。

嵌套匿名结构体的基本语法

type Goods struct {
Name string
Price int
}
type Book struct {
Goods //这里就是嵌套匿名结构体 Goods
Writer string
}

继承给编程带来的便利

  1. 代码的复用性提高了
  2. 代码的扩展性和维护性提高了

继承的深入讨论

  1. 结构体可以 使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段、方法,

    都可以使用。【举例说明】
  2. 匿名结构体字段访问可以简化
package main

import (
"fmt"
) type A struct {
Name string
age int
} func (a *A) SayOk() {
fmt.Println("A SayOk", a.Name)
} func (a *A) hello() {
fmt.Println("A hello", a.Name)
} type B struct {
A
Name string
} func (b *B) SayOk() {
fmt.Println("B SayOk", b.Name)
} func main() { // var b B
// b.A.Name = "tom"
// b.A.age = 19
// b.A.SayOk()
// b.A.hello() // //上面的写法可以简化 // b.Name = "smith"
// b.age = 20
// b.SayOk()
// b.hello() var b B
b.Name = "jack" // ok
b.A.Name = "scott"
b.age = 100 //ok
b.SayOk() // B SayOk jack
b.A.SayOk() // A SayOk scott
b.hello() // A hello ? "jack" 还是 "scott" }

对上面的代码小结

(1) 当我们直接通过 b 访问字段或方法时,其执行流程如下比如 b.Name

(2) 编译器会先看 b 对应的类型有没有 Name, 如果有,则直接调用 B 类型的 Name 字段

(3) 如果没有就去看 B 中嵌入的匿名结构体 A 有没有声明 Name 字段,如果有就调用,如果没有

继续查找..如果都找不到就报错.

3) 当 结构体和 匿名结构体有相同的字段或者方法时, 编译器采用就近访问原则访问,如希望访问

匿名结构体的字段和方法,可以通过匿名结构体名来区分【举例说明】

4) 结构体嵌入两个(或多个)匿名结构体,如 两个匿名结构体有相同的字段和方法( 同时结构体本身

没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错。【举例说明】

5) 如果一个 struct 嵌套了一个有名结构体,这种模式就是 组合,如果是组合关系,那么在访问组合

的结构体的字段或方法时,必须带上结构体的名字

6) 嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个 匿名结构体字段的值

说明

  1. 如果一个结构体有 int 类型的匿名字段,就不能第二个。
  2. 如果需要有多个 int 的字段,则必须给 int 字段指定名字

面向对象编程-多重继承

多重继承说明

如 一个 struct 嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方

法, 从而实现了多重继承。

多重继承细节说明

  1. 如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来

    区分。【案例演示】
  2. 为了保证代码的简洁性,建议大家尽量不使用多重继承
package main
import (
"fmt"
) type A struct {
Name string
age int
}
type B struct {
Name string
Score float64
}
type C struct {
A
B
//Name string
} type D struct {
a A //有名结构体
} type Goods struct {
Name string
Price float64
} type Brand struct {
Name string
Address string
} type TV struct {
Goods
Brand
} type TV2 struct {
*Goods
*Brand
} type Monster struct {
Name string
Age int
} type E struct {
Monster
int
n int
} func main() {
var c C
//如果c 没有Name字段,而A 和 B有Name, 这时就必须通过指定匿名结构体名字来区分
//所以 c.Name 就会包编译错误, 这个规则对方法也是一样的!
c.A.Name = "tom" // error
fmt.Println("c") //如果D 中是一个有名结构体,则访问有名结构体的字段时,就必须带上有名结构体的名字
//比如 d.a.Name
var d D
d.a.Name = "jack" //嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
tv := TV{ Goods{"电视机001", 5000.99}, Brand{"海尔", "山东"}, } //演示访问Goods的Name
fmt.Println(tv.Goods.Name)
fmt.Println(tv.Price) tv2 := TV{
Goods{
Price : 5000.99,
Name : "电视机002",
},
Brand{
Name : "夏普",
Address :"北京",
},
} fmt.Println("tv", tv)
fmt.Println("tv2", tv2) tv3 := TV2{ &Goods{"电视机003", 7000.99}, &Brand{"创维", "河南"}, } tv4 := TV2{
&Goods{
Name : "电视机004",
Price : 9000.99,
},
&Brand{
Name : "长虹",
Address : "四川",
},
} fmt.Println("tv3", *tv3.Goods, *tv3.Brand)
fmt.Println("tv4", *tv4.Goods, *tv4.Brand) //演示一下匿名字段时基本数据类型的使用
var e E
e.Name = "狐狸精"
e.Age = 300
e.int = 20
e.n = 40
fmt.Println("e=", e) }

接口(interface)

基本介绍

按顺序,我们应该学习多态,但是在学习多态前,我们需要讲解接口(interface),因为在 Golang 中 多态

特性主要是通过接口来体现的。

接口快速入门

这样的设计需求在 Golang 编程中也是会大量存在的,我曾经说过,一个程序就是一个世界,在现实世

界存在的情况,在程序中也会出现。 我们用程序来模拟一下前面的应用场景。

代码实现

package main
import (
"fmt"
) //声明/定义一个接口
type Usb interface {
//声明了两个没有实现的方法
Start()
Stop()
} //声明/定义一个接口
type Usb2 interface {
//声明了两个没有实现的方法
Start()
Stop()
Test()
} type Phone struct { } //让Phone 实现 Usb接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作。。。")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作。。。")
} type Camera struct { }
//让Camera 实现 Usb接口的方法
func (c Camera) Start() {
fmt.Println("相机开始工作~~~。。。")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作。。。")
} //计算机
type Computer struct { } //编写一个方法Working 方法,接收一个Usb接口类型变量
//只要是实现了 Usb接口 (所谓实现Usb接口,就是指实现了 Usb接口声明所有方法)
func (c Computer) Working(usb Usb) { //通过usb接口变量来调用Start和Stop方法
usb.Start()
usb.Stop()
} func main() { //测试
//先创建结构体变量
computer := Computer{}
phone := Phone{}
camera := Camera{} //关键点
computer.Working(phone)
computer.Working(camera) //
}

接口概念的再说明

interface 类型可以定义一组方法,但是这些不需要实现。并且 interface 不能包含任何变量。到某个

自定义类型(比如结构体 Phone)要使用的时候,在根据具体情况把这些方法写出来(实现)。

说明

  1. 接口里的 所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的

    多态和 高内聚低偶合的思想。
  2. Golang 中的接口, 不需要 显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个

    变量就实现这个接口。因此,Golang 中 没有 implement 这样的关键字

注意事项和细节

  1. 接口本身 不能创建实例,但是 可以指向一个实现了该接口的自定义类型的变量(实例)
  2. 接口中所有的方法都没有方法体,即都是没有实现的方法。
  3. 在 Golang 中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。
  4. 一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
  5. 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
  6. 一个自定义类型可以实现多个接口
  7. Golang 接口中不能有任何变量
  8. 一个接口(比如 A 接口)可以继承多个别的接口(比如 B,C 接口),这时如果要实现 A 接口,也必须将 B,C 接口的方法也全部实现。
  9. interface 类型默认是一个指针(引用类型),如果没有对 interface 初始化就使用,那么会输出 nil
  10. 空接口 interface{} 没有任何方法, 所以所有类型都实现了空接 口, 即我们可以 把任何一个变量

    赋给空接口。
package main
import (
"fmt"
) type Stu struct {
Name string
} func (stu Stu) Say() {
fmt.Println("Stu Say()")
} type integer int func (i integer) Say() {
fmt.Println("integer Say i =" ,i )
} type AInterface interface {
Say()
} type BInterface interface {
Hello()
}
type Monster struct { }
func (m Monster) Hello() {
fmt.Println("Monster Hello()~~")
} func (m Monster) Say() {
fmt.Println("Monster Say()~~")
} func main() {
var stu Stu //结构体变量,实现了 Say() 实现了 AInterface
var a AInterface = stu
a.Say() var i integer = 10
var b AInterface = i
b.Say() // integer Say i = 10 //Monster实现了AInterface 和 BInterface
var monster Monster
var a2 AInterface = monster
var b2 BInterface = monster
a2.Say()
b2.Hello()
}
package main
import (
"fmt"
) type BInterface interface {
test01()
} type CInterface interface {
test02()
} type AInterface interface {
BInterface
CInterface
test03()
} //如果需要实现AInterface,就需要将BInterface CInterface的方法都实现
type Stu struct {
}
func (stu Stu) test01() { }
func (stu Stu) test02() { }
func (stu Stu) test03() { } type T interface{ } func main() {
var stu Stu
var a AInterface = stu
a.test01() var t T = stu //ok
fmt.Println(t)
var t2 interface{} = stu
var num1 float64 = 8.8
t2 = num1
t = num1
fmt.Println(t2, t)
}
package main
import "fmt"
type Usb interface {
Say()
}
type Stu struct {
}
func (this *Stu) Say() {
fmt.Println("Say()")
}
func main() {
var stu Stu = Stu{}
// 错误! 会报 Stu类型没有实现Usb接口 ,
// 如果希望通过编译, var u Usb = &stu
var u Usb = stu
u.Say()
fmt.Println("here", u)
}

接口编程的最佳实践

实现对 Hero 结构体切片的排序: sort.Sort(data Interface)

package main
import (
"fmt"
"sort"
"math/rand"
) //1.声明Hero结构体
type Hero struct{
Name string
Age int
} //2.声明一个Hero结构体切片类型
type HeroSlice []Hero //3.实现Interface 接口
func (hs HeroSlice) Len() int {
return len(hs)
} //Less方法就是决定你使用什么标准进行排序
//1. 按Hero的年龄从小到大排序!!
func (hs HeroSlice) Less(i, j int) bool {
return hs[i].Age < hs[j].Age
//修改成对Name排序
//return hs[i].Name < hs[j].Name
} func (hs HeroSlice) Swap(i, j int) {
//交换
// temp := hs[i]
// hs[i] = hs[j]
// hs[j] = temp
//下面的一句话等价于三句话
hs[i], hs[j] = hs[j], hs[i]
} //1.声明Student结构体
type Student struct{
Name string
Age int
Score float64
} //将Student的切片,安Score从大到小排序!! func main() { //先定义一个数组/切片
var intSlice = []int{0, -1, 10, 7, 90}
//要求对 intSlice切片进行排序
//1. 冒泡排序...
//2. 也可以使用系统提供的方法
sort.Ints(intSlice)
fmt.Println(intSlice) //请大家对结构体切片进行排序
//1. 冒泡排序...
//2. 也可以使用系统提供的方法 //测试看看我们是否可以对结构体切片进行排序
var heroes HeroSlice
for i := 0; i < 10 ; i++ {
hero := Hero{
Name : fmt.Sprintf("英雄|%d", rand.Intn(100)),
Age : rand.Intn(100),
}
//将 hero append到 heroes切片
heroes = append(heroes, hero)
} //看看排序前的顺序
for _ , v := range heroes {
fmt.Println(v)
} //调用sort.Sort
sort.Sort(heroes)
fmt.Println("-----------排序后------------")
//看看排序后的顺序
for _ , v := range heroes {
fmt.Println(v)
} i := 10
j := 20
i, j = j, i
fmt.Println("i=", i, "j=", j) // i=20 j = 10
}

实现接口 vs 继承

大家可能会对实现接口和继承比较迷茫了, 那么他们究竟有什么区别呢

package main
import (
"fmt"
) //Monkey结构体
type Monkey struct {
Name string
} //声明接口
type BirdAble interface {
Flying()
} type FishAble interface {
Swimming()
} func (this *Monkey) climbing() {
fmt.Println(this.Name, " 生来会爬树..")
} //LittleMonkey结构体
type LittleMonkey struct {
Monkey //继承
} //让LittleMonkey实现BirdAble
func (this *LittleMonkey) Flying() {
fmt.Println(this.Name, " 通过学习,会飞翔...")
} //让LittleMonkey实现FishAble
func (this *LittleMonkey) Swimming() {
fmt.Println(this.Name, " 通过学习,会游泳..")
} func main() { //创建一个LittleMonkey 实例
monkey := LittleMonkey{
Monkey {
Name : "悟空",
},
}
monkey.climbing()
monkey.Flying()
monkey.Swimming() }
  1. 当 A 结构体继承了 B 结构体,那么 A 结构就自动的继承了 B 结构体的字段和方法,并且可以直

    接使用
  2. 当 A 结构体需要扩展功能,同时不希望去破坏继承关系,则可以去实现某个接口即可,因此我们可以认为:实现接口是对继承机制的补充.实现接口可以看作是对 继承的一种补充

接口和继承解决的解决的问题不同

继承的价值主要在于:解决代码的 复用性和 可维护性。

接口的价值主要在于: 设计,设计好各种规范(方法),让其它自定义类型去实现这些方法。

接口比继承更加灵活 Person Student BirdAble LittleMonkey

接口比继承更加灵活,继承是满足 is - a 的关系,而接口只需满足 like - a 的关系。

接口在一定程度上实现 代码解耦

面向对象编程-多态

基本介绍

变量(实例)具有多种形态。面向对象的第三大特征,在 Go 语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。

快速入门

在前面的 Usb 接口案例,Usb usb ,既可以接收手机变量,又可以接收相机变量,就体现了 Usb 接口 多态特性。

接口体现多态的两种形式

多态参数

在前面的 Usb 接口案例,Usb usb ,即可以接收手机变量,又可以接收相机变量,就体现了 Usb 接多态。

多态数组

演示一个案例:给 Usb 数组中,存放 Phone 结构体 和 Camera 结构体变量

案例说明:

package main
import (
"fmt"
) //声明/定义一个接口
type Usb interface {
//声明了两个没有实现的方法
Start()
Stop()
} type Phone struct {
name string
} //让Phone 实现 Usb接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作。。。")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作。。。")
} type Camera struct {
name string
}
//让Camera 实现 Usb接口的方法
func (c Camera) Start() {
fmt.Println("相机开始工作。。。")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作。。。")
} func main() {
//定义一个Usb接口数组,可以存放Phone和Camera的结构体变量
//这里就体现出多态数组
var usbArr [3]Usb
usbArr[0] = Phone{"vivo"}
usbArr[1] = Phone{"小米"}
usbArr[2] = Camera{"尼康"} fmt.Println(usbArr) }

类型断言

由一个具体的需要,引出了类型断言

基本介绍

类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言,

具体的如下:

package main
import (
"fmt"
)
type Point struct {
x int
y int
}
func main() {
var a interface{}
var point Point = Point{1, 2}
a = point //oK
// 如何将 a 赋给一个Point变量?
var b Point
// b = a 不可以
// b = a.(Point) // 可以
b = a.(Point)
fmt.Println(b) // //类型断言的其它案例
// var x interface{}
// var b2 float32 = 1.1
// x = b2 //空接口,可以接收任意类型
// // x=>float32 [使用类型断言]
// y := x.(float32)
// fmt.Printf("y 的类型是 %T 值是=%v", y, y) //类型断言(带检测的)
var x interface{}
var b2 float32 = 2.1
x = b2 //空接口,可以接收任意类型
// x=>float32 [使用类型断言] //类型断言(带检测的)
if y, ok := x.(float32); ok {
fmt.Println("convert success")
fmt.Printf("y 的类型是 %T 值是=%v", y, y)
} else {
fmt.Println("convert fail")
}
fmt.Println("继续执行...") }

对上面代码的说明:

在进行类型断言时,如果类型不匹配,就会报 panic, 因此进行类型断言时,要确保原来的空接口

指向的就是断言的类型.

如何在进行断言时,带上检测机制,如果成功就 ok,否则也不要报 panic

类型断言的最佳实践 1

在前面的 Usb 接口案例做改进:

给 Phone 结构体增加一个特有的方法 call(), 当 Usb 接口接收的是 Phone 变量时,还需要调用 call

方法, 走代码:

package main
import (
"fmt"
) //声明/定义一个接口
type Usb interface {
//声明了两个没有实现的方法
Start()
Stop()
} type Phone struct {
name string
} //让Phone 实现 Usb接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作。。。")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作。。。")
} func (p Phone) Call() {
fmt.Println("手机 在打电话..")
} type Camera struct {
name string
}
//让Camera 实现 Usb接口的方法
func (c Camera) Start() {
fmt.Println("相机开始工作。。。")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作。。。")
} type Computer struct { } func (computer Computer) Working(usb Usb) {
usb.Start()
//如果usb是指向Phone结构体变量,则还需要调用Call方法
//类型断言..[注意体会!!!]
if phone, ok := usb.(Phone); ok {
phone.Call()
}
usb.Stop()
} func main() {
//定义一个Usb接口数组,可以存放Phone和Camera的结构体变量
//这里就体现出多态数组
var usbArr [3]Usb
usbArr[0] = Phone{"vivo"}
usbArr[1] = Phone{"小米"}
usbArr[2] = Camera{"尼康"} //遍历usbArr
//Phone还有一个特有的方法call(),请遍历Usb数组,如果是Phone变量,
//除了调用Usb 接口声明的方法外,还需要调用Phone 特有方法 call. =》类型断言
var computer Computer
for _, v := range usbArr{
computer.Working(v)
fmt.Println()
}
//fmt.Println(usbArr)
}

类型断言的最佳实践 2

写一函数,循环判断传入参数的类型:

package main
import (
"fmt"
) //定义Student类型
type Student struct { } //编写一个函数,可以判断输入的参数是什么类型
func TypeJudge(items... interface{}) {
for index, x := range items {
switch x.(type) {
case bool :
fmt.Printf("第%v个参数是 bool 类型,值是%v\n", index, x)
case float32 :
fmt.Printf("第%v个参数是 float32 类型,值是%v\n", index, x)
case float64 :
fmt.Printf("第%v个参数是 float64 类型,值是%v\n", index, x)
case int, int32, int64 :
fmt.Printf("第%v个参数是 整数 类型,值是%v\n", index, x)
case string :
fmt.Printf("第%v个参数是 string 类型,值是%v\n", index, x)
case Student :
fmt.Printf("第%v个参数是 Student 类型,值是%v\n", index, x)
case *Student :
fmt.Printf("第%v个参数是 *Student 类型,值是%v\n", index, x)
default :
fmt.Printf("第%v个参数是 类型 不确定,值是%v\n", index, x)
}
}
} func main() { var n1 float32 = 1.1
var n2 float64 = 2.3
var n3 int32 = 30
var name string = "tom"
address := "北京"
n4 := 300 stu1 := Student{}
stu2 := &Student{} TypeJudge(n1, n2, n3, name, address, n4, stu1, stu2) }

go的面向对象终于完了()!~~~~

go-面向对象编程(下)的更多相关文章

  1. Go语言基础之接口(面向对象编程下)

    1 接口 1.1 接口介绍 接口(interface)是Go语言中核心部分,Go语言提供面向接口编程,那么接口是什么? 现实生活中,有许多接口的例子,比如说电子设备上的充电接口,这个充电接口能干什么, ...

  2. python面向对象编程(下)

    本篇详细介绍了Python 中类的成员.成员修饰符.类的特殊成员以及两个综合运用实例. 环境为:python3.5.1 类的成员 类的成员包括三大类:字段.方法和property属性 注:关于这三类成 ...

  3. OOP面向对象编程(下)

    我们怎么去模拟重载,在javasceipr中我们可以通过参数的类型区别或者数量的区别,来去让同样一个函数名字,可以根据不同的参数列表的情况来去调用相应的函数. javascript中函数类型是不确定的 ...

  4. C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域

    面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:假设不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这样的类作用域的层次嵌套使 ...

  5. Java面向对象 网络编程 下

    Java面向对象 网络编程  下 知识概要:                   (1)Tcp 练习 (2)客户端向服务端上传一个图片. (3) 请求登陆 (4)url 需求:上传图片. 客户端:   ...

  6. Python面向对象编程(下)

    本文主要通过几个实例介绍Python面向对象编程中的封装.继承.多态三大特性. 封装性 我们还是继续来看下上文中的例子,使用Student类创建一个对象,并修改对象的属性.代码如下: #-*- cod ...

  7. angular2系列教程(六)两种pipe:函数式编程与面向对象编程

    今天,我们要讲的是angualr2的pipe这个知识点. 例子

  8. PHP 面向对象编程和设计模式 (1/5) - 抽象类、对象接口、instanceof 和契约式编程

    PHP高级程序设计 学习笔记 2014.06.09 什么是面向对象编程 面向对象编程(Object Oriented Programming,OOP)是一种计算机编程架构.OOP 的一条基本原则是计算 ...

  9. python基础-面向对象编程

    一.三大编程范式 编程范式即编程的方法论,标识一种编程风格 三大编程范式: 1.面向过程编程 2.函数式编程 3.面向对象编程 二.编程进化论 1.编程最开始就是无组织无结构,从简单控制流中按步写指令 ...

  10. python 学习笔记7 面向对象编程

    一.概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发"更快更好更强..." ...

随机推荐

  1. 如何设计APP版本号?

    示例: 2.14.21 (主版本号.次版本号.补丁号) 我们可以这样设计,软件包的版本号以英文句号分隔的三个数字来定义,分别代表主版本号.次版本号和补丁号.如果只是修复了错误,没有添加任何功能,也不会 ...

  2. 多进程操作-进程队列multiprocess.Queue的使用

    一.ipc机制 进程通讯 管道:pipe 基于共享的内存空间 队列:pipe+锁 queue 下面拿代码来实现Queue如何使用: 案例一: from multiprocessing import Q ...

  3. Kafka简明教程

    作者:柳树之 www.jianshu.com/p/7b77723d4f96 Kafka是啥?用Kafka官方的话来说就是: Kafka is used for building real-time d ...

  4. Actor模型(分布式编程)

    Actor的目的是为了解决分布式编程中的一系列问题.所有消息都是异步交付的,因此将消息发送方与接收方分开,正是由于这种分离,导致actor系统具有内在的并发性:可以不受限制地并行执行任何拥有输入消息的 ...

  5. PostgreSQL update set from 两表联合更新,注意与其它数据库更新语法有差别

    最近用PostgreSql数据库进行表关联更新时,发现与之前用的Sql Server 和My Sql语法有很大差别,稍微不注意,很容易出错. PostgreSql表更新时,两个表只允许一个表起别名,一 ...

  6. Wappalyzer(chrome网站分析插件)

    Wappalyzer是一款功能强大的.且非常实用的chrome网站技术分析插件,通过该插件能够分析目标网站所采用的平台构架. 网站环境.服务器配置环境.JavaScript框架.编程语言等参数,使用时 ...

  7. js分号问题

    总结一句话: 一行开头是括号(比如IIFE)或者方括号的时候加上分号就可以

  8. JS 实现

    JavaScript 使用 HTML 中的脚本必须位于<script> 与 </script>标签之间. 脚本可被放置在 HTML 页面的 <body>和 < ...

  9. 基于opencv -python--银行卡识别

    import cv2 def sort_contours(cnts, method="left-to-right"): reverse = False i = 0 if metho ...

  10. Linux相关集合

    本篇概述 Linux xshell6 连接 Hadoop 启动关闭 Linux xshell6 连接相关问题 首先,虚拟机 得先能通成网(具体教程可百度) 然后,进行 本机 ip 的查询(xshell ...