GO学习笔记——封装、继承、多态

GO语言有着面向对象编程的三大特性——封装、继承、多态,只是实现方式不同,本文只介绍实现方式,不会特别详细介绍特性。如果对三大特性没有概念,可以先去看面向对象编程特性相关知识。


一、封装

封装就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作才能对字段进行操作。

注意:GO开发中并没有特别强调封装,这与Java不同,不能总是用Java的语法特性来看待GO,GO本身对面向对象的特性进行了简化。

1、GO体现封装的方式

        1)对结构体中的属性进行封装

        2)通过方法、包是实现封装

2、工厂模式

GO中的结构体没有构造函数,通常使用工厂模式来实现相同功能。

工厂模式:面向对象编程的设计模式之一。在工厂模式中,创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

以下为GO中实现工厂模式的一个例子:

package pizzaPackage

type pizza struct {
	name  string
	price int
}

func (p *pizza) SetName(name string) {
	p.name = name
}

func (p *pizza) GetName() string {
	return (*p).name
}

func (p *pizza) SetPrice(price int) {
	p.price = price
}

func (p *pizza) GetPrice() int {
	return (*p).price
}

func MakePizza(name string, price int) *pizza {
	return &pizza{
		name:  name,
		price: price,
	}
}

然后在另一个包中调用这个包:

package main

import (
	"fmt"
	pizza "project2/main/pizzaPackage"
)

func main() {
	p := pizza.MakePizza("榴莲披萨", 40)
	fmt.Println("我花", p.GetPrice(), "吃到了", p.GetName())
}

代码执行结果如下:

上面我们创建了一个pizzaPackage包,但是不希望外部直接看到并使用pizza结构体的内部,所以提供了一个工厂方法MakePizza(),其他包调用这个方法就可直接得到一个pizza类型并使用这个结构体所公开的所有方法和属性,但私有的属性和方法是不可以使用的,这其实已经体现了封装的特性。

3、封装的实现步骤

1)将结构体、字段的首字母小写(private)

2)给结构体所在的包提供一个工厂模式的函数,首字母大写

3)提供一个首字母大写的Set方法,用于对属性判断与赋值

4)提供一个首字母大写的Get方法,用于获取属性的值

4、举个例子

其实上述第2点工厂模式的例子中已经体现了封装的特性与步骤,这里不另外举例了。


二、继承

1、GO中继承的实现——嵌套匿名结构体

基本语法:

type 父类结构体名 struct{

        field1 type

        field2 type

        ……

}

type 子类结构体名 struct{

        父类结构体名

        field1 type

        ……

}

在子类结构体中,父类结构体就是被嵌套的匿名结构体

2、举个例子

type Animal struct {
	Name  string
	Age   int
	Food  string
	Drink string
}

type Alive interface {
	eat()
	drink()
}

func (A *Animal) eat() {
	fmt.Println(A.Name, "吃", A.Food)
}

func (A *Animal) drink() {
	fmt.Println(A.Name, "喝", A.Drink)
}

type Dog struct {
	Animal
}

type Cat struct {
	Animal
}

func main() {
	dog := Dog{
		Animal{
			Name:  "小狗",
			Age:   5,
			Food:  "骨头",
			Drink: "水",
		},
	}

	cat := Cat{Animal{"小猫", 5, "鱼", "牛奶"}}

	dog.Animal.drink()
	dog.eat()

	cat.drink()
	cat.Animal.eat()
}

上述代码的输出为:

3、注意事项说明

1)结构体可以使用嵌套结构体的所有方法和字段,不管是否大小写

2)当只嵌套一个匿名结构体时,如果访问嵌套结构体内的字段,内部匿名结构体名可以省略,即A.B.field与A.field都可以;当结构体和匿名结构体有同名字段或方法时,编译器采用就近访问原则

3)当嵌套的匿名结构体数大于等于2时,如果这些匿名结构体有相同字段和方法并且外侧结构体没有时,必须显式的指明所访问内部结构体,即A嵌套了B和C时,A.B.field和A.C.field要指明B、C

4)当嵌套匿名结构体数大于等于2时,就实现了多重继承,但不建议使用


三、多态

GO的多态是通过接口来体现,可以按照统一的接口来调用不同的实现,这个时候接口变量就体现不同的形态。

1、GO中多态的实现——接口体现多态的两种形式

1)多态参数

声明一个接口类型的实例,然后将实现了接口的不同结构体赋值这个接口类型实例,举个例子:

type Animal struct {
	Name  string
	Age   int
	Food  string
	Drink string
}

type Alive interface {
	eat()
	drink()
}

func (A Animal) eat() {
	fmt.Println(A.Name, "吃", A.Food)
}

func (A Animal) drink() {
	fmt.Println(A.Name, "喝", A.Drink)
}

type Dog struct {
	Animal
}

type Cat struct {
	Animal
}

func main() {

	var life Alive    //声明了一个接口变量

    animal := Animal{"生物", 10, "食物", "水"}

	dog := Dog{
		Animal{
			Name:  "小狗",
			Age:   5,
			Food:  "骨头",
			Drink: "水",
		},
	}

	cat := Cat{Animal{"小猫", 5, "鱼", "牛奶"}}

	life = animal    //将实现了接口的Animal类给接口
	life.drink()
	life.eat()

	life = dog    //将实现了接口的Animal类的子类给接口
	life.drink()
	life.eat()

	life = cat
	life.drink()
	life.eat()

}

上述代码的输出为:

 

2)多态数组

用接口数组来存放不同的结构体,举个例子:

type Animal struct {
	Name  string
	Age   int
	Food  string
	Drink string
}

type Alive interface {
	eat()
	drink()
}

func (A Animal) eat() {
	fmt.Println(A.Name, "吃", A.Food)
}

func (A Animal) drink() {
	fmt.Println(A.Name, "喝", A.Drink)
}

type Dog struct {
	Animal
}

type Cat struct {
	Animal
}

func main() {

	var life [3]Alive

	life[0] = Dog{
		Animal{
			Name:  "小狗",
			Age:   5,
			Food:  "骨头",
			Drink: "水",
		},
	}

	life[1] = Cat{Animal{"小猫", 5, "鱼", "牛奶"}}

	life[2] = Animal{"生物", 10, "食物", "水"}

	for i := 0; i < 3; i++ {
		life[i].eat()
		life[i].drink()
	}

}

上述代码的输出为:

2、类型断言

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

举个简单的例子:

var x interface{}
var b2 float32 = 1.23
x = b2
y := x.(float32)    //float32类型断言
fmt.Printf("y的类型是 %T,值是%v",y,y)

一点说明:

        在进行类型断言时,如果类型不匹配,就会报panic,因此:

        1)进行断言时,要确保原来的空接口指向的就是断言类型

        2)或者,进行断言时,进行检测,如果成功就ok,否则也不要报panic,如下:

var x interface{}
var b2 float32 = 1.23
x = b2
y := x.(float32)    //float32类型断言

if y, ok := x.(float32);ok{
    fmt.Printf("y的类型是 %T,值是%v",y,y)
} else {
    fmt.Printf("匹配失败")
}

再举一个类型断言的常用示例(在前面Animal例子中加入一个Active()函数):

func Active(life Alive) {
	switch life.(type) {
	case Dog:
		fmt.Println("小狗汪汪叫")
	case Cat:
		fmt.Println("小猫爬上树")
	case Animal:
		fmt.Println("动物真可爱")
	default:
		fmt.Println("不明生命体")
	}

}

func main() {

	var life [3]Alive

	life[0] = Dog{
		Animal{
			Name:  "小狗",
			Age:   5,
			Food:  "骨头",
			Drink: "水",
		},
	}

	life[1] = Cat{Animal{"小猫", 5, "鱼", "牛奶"}}

	life[2] = Animal{"生物", 10, "食物", "水"}

	for i := 0; i < 3; i++ {
		Active(life[i])
	}

}

上述代码的输出为:


  • 23
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
封装继承多态是面向对象编程中的三个重要概念。 封装是指将数据和对数据的操作封装在一个类中,通过访问修饰符来控制对数据的访问权限。这样可以隐藏内部实现细节,提高代码的安全性和可维护性。\[2\] 继承是指一个类可以继承另一个类的属性和方法。通过继承,子类可以重用父类的代码,并且可以在子类中添加新的属性和方法。这样可以提高代码的复用性和可扩展性。\[2\] 多态是指同一个方法在不同的对象上可以有不同的行为。通过多态,可以实现方法的重写和重载,使得程序可以根据对象的实际类型来调用相应的方法。这样可以提高代码的灵活性和可扩展性。\[1\] 总结起来,封装继承多态是面向对象编程的三个基本特性,它们可以使代码更加模块化、可维护和可扩展。 #### 引用[.reference_title] - *1* *2* [封装继承多态](https://blog.csdn.net/yahid/article/details/125665027)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [C++ 类的封装继承多态](https://blog.csdn.net/secondtonone1/article/details/124485035)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值