Golang面向对象编程(二)

2024-06-04 6446阅读

文章目录

  • 封装
    • 基本介绍
    • 封装的实现
    • 工厂函数
    • 继承
      • 基本介绍
      • 继承的实现
      • 字段和方法访问细节
      • 多继承

        封装

        基本介绍

        基本介绍

        • 封装(Encapsulation)是面向对象编程(OOP)中的一种重要概念,封装通过将数据和相关的方法组合在一起,形成一个称为类的抽象数据类型,只暴露必要的接口供外部使用。
        • 封装可以隐藏数据的实际实现细节,外部只能通过公共(public)接口来访问和修改数据,使得代码更加模块化和结构化,同时可以防止不恰当的访问和操作,提高数据的安全性。
        • 封装将相关的数据和方法组织在一起,形成了一个独立的单元,外部使用者只需关心公共接口,无需了解内部实现细节,简化了使用方式,提高了代码的可读性和可维护性。
        • 封装使得内部实现可以独立于外部接口进行修改,如果内部实现发生了变化,只需要确保公共接口的兼容性,而不会影响使用该类的其他代码,提供了更好的灵活性和可扩展性。

          封装的实现

          封装的实现

          • Go中的封装是通过命名约定和访问控制来实现的,而不像一些其他面向对象语言那样使用访问修饰符(如public、private、protected),因此开发者需要自觉遵守约定来保持封装的效果。
          • Go中通过结构体将相关的字段和方法组合在一起,并通过首字母大小写来控制其可访问性。结构体中的字段和方法使用大写字母开头表示公共的(可导出的),可以被其他包访问,而使用小写字母开头表示私有的(不可导出的),只能在当前包内使用。
          • Go中的封装更加宽泛,其封装的基本单元不是结构体而是包(package),包内的所有标识符(变量、函数、结构体、方法等)都通过首字母大小写来控制其可访问性

            封装案例如下:

            package model
            import "fmt"
            type Student struct {
            	name   string
            	age    int
            	gender string
            }
            // 访问name字段
            func (stu Student) GetName() string {
            	return stu.name
            }
            func (stu *Student) SetName(name string) {
            	stu.name = name
            }
            // 访问age字段
            func (stu Student) GetAge() int {
            	return stu.age
            }
            func (stu *Student) SetAge(age int) {
            	stu.age = age
            }
            // 访问gender字段
            func (stu Student) GetGender() string {
            	return stu.gender
            }
            func (stu *Student) SetGender(gender string) {
            	stu.gender = gender
            }
            // Student的其他方法
            func (stu Student) Print() {
            	fmt.Printf("student info: \n",
            		stu.name, stu.age, stu.gender)
            }
            

            使用上述包内结构体的案例如下:

            package main
            import (
            	"go_code/OOP2/Encapsulate/model"
            )
            func main() {
            	// var stu = model.Student{"Alice", 12, "女"} // 隐式赋值
            	var stu model.Student
            	stu.SetName("Alice")
            	stu.SetAge(12)
            	stu.SetGender("女")
            	stu.Print() // student info: 
            }
            

            注意: Go中无法对结构体中不可导出的字段进行隐式赋值,可以通过给结构体绑定对应的setter和getter方法对不可导出的字段进行访问和赋值。

            工厂函数

            工厂函数

            如果结构体类型的首字母小写(不可导出),那么其他包将无法直接使用该结构体类型来创建实例,这时可以在包内提供对应可导出的工厂函数来创建实例并返回。如下:

            package model
            import "fmt"
            type student struct {
            	name   string
            	age    int
            	gender string
            }
            // 工厂函数
            func NewStudent(name string, age int, gender string) *student {
            	return &student{
            		name:   name,
            		age:    age,
            		gender: gender,
            	}
            }
            func (stu student) Print() {
            	fmt.Printf("student info: \n",
            		stu.name, stu.age, stu.gender)
            }
            

            其他包通过调用包中可导出的工厂函数,即可创建对应不可导出的结构体实例。如下:

            package main
            import (
            	"go_code/OOP2/Encapsulate/model"
            )
            func main() {
            	var stu = model.NewStudent("Alice", 12, "女")
            	stu.Print() // student info: 
            }
            

            继承

            基本介绍

            基本介绍

            • 继承是面向对象编程中的一个重要概念,其允许一个类(子类/派生类)继承另一个类(父类/基类)的属性和方法,子类继承父类后可以直接访问和使用父类中字段和方法,同时可以添加自己的字段和方法。
            • 继承的主要优势在于代码复用,继承可以在不重复编写相似代码的情况下扩展现有的类,使代码更具可维护性和可扩展性。

              继承示意图如下:

              Golang面向对象编程(二) 第1张

              继承的实现

              继承的实现

              • Go中的继承是通过嵌套匿名结构体的方式来实现的,如果一个结构体中嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体中的字段和方法,从而实现了继承的效果。

                继承案例如下:

                package main
                import (
                	"fmt"
                )
                // 基类
                type Person struct {
                	Name string
                	Age  int
                }
                func (per Person) PrintInfo() {
                	fmt.Printf("name = %s, age = %d\n", per.Name, per.Age)
                }
                // 派生类
                type Student struct {
                	Person    // 嵌套匿名结构体
                	StudentId int
                }
                func (stu Student) Study() {
                	fmt.Printf("student %d is studying...\n", stu.StudentId)
                }
                // 派生类
                type Teacher struct {
                	*Person   // 嵌套匿名结构体指针
                	TeacherId int
                }
                func (tch Teacher) Teach() {
                	fmt.Printf("teacher %d is teaching...\n", tch.TeacherId)
                }
                func main() {
                	var stu = Student{Person{"Alice", 12}, 100}
                	stu.PrintInfo() // name = Alice, age = 12
                	stu.Study()     // student 100 is studying...
                	var tch = Teacher{&Person{"Bob", 22}, 200}
                	tch.PrintInfo() // name = Bob, age = 22
                	tch.Teach()     // teacher 200 is teaching...
                }
                

                说明一下:

                • 在嵌套匿名结构体时,可以通过Type的方式嵌套匿名结构体,也可以通过*Type的方式嵌套匿名结构体指针。
                • 在创建结构体变量时,如果要通过字段名的方式初始化结构体字段,那么匿名结构体的字段名由匿名结构体的类型名充当。
                • 在结构体中嵌套匿名结构体后,可以通过结构体实例访问匿名结构体的字段和方法,但在访问时仍然遵循Go的命名约定和访问控制。如果被嵌套的匿名结构体的定义在其他包,那么通过结构体实例只能访问匿名结构体可导出的字段和方法。
                • 结构体中嵌入的匿名字段也可以是基本数据类型,在访问结构体中的匿名基本数据类型字段时,以对应基本数据类型的类型名作为其字段名。比如结构体中嵌入了一个匿名int字段,则通过结构体变量名.int的方式对其进行访问。

                  组合

                  在结构体中嵌套有名结构体属于组合关系,在访问组合的结构体字段和方法时,必须带上结构体的字段名。如下:

                  package main
                  import (
                  	"fmt"
                  )
                  // 车轮
                  type Wheel struct {
                  	Color string
                  	price int
                  }
                  // 自行车
                  type Bicycle struct {
                  	FrontWheel Wheel // 组合
                  	RearWheel  Wheel // 组合
                  	// ...
                  }
                  func (bc Bicycle) Print() {
                  	fmt.Printf("前轮 后轮\n",
                  		bc.FrontWheel.Color, bc.FrontWheel.price, bc.RearWheel.Color, bc.RearWheel.price)
                  }
                  func main() {
                  	var bc = Bicycle{
                  		FrontWheel: Wheel{"black", 100},
                  		RearWheel:  Wheel{"blue", 200},
                  	}
                  	bc.Print() // 前轮 后轮
                  }
                  

                  字段和方法访问细节

                  字段和方法访问细节

                  结构体字段和方法的访问流程:

                  1. 先查看当前结构体类型中是否有对应的字段或方法,如果有则访问。
                  2. 再查看结构体中嵌入的匿名结构体中是否有对应的字段或方法,如果有则访问。
                  3. 继续查找更深层次嵌入的匿名结构体中是否有对应的字段或方法,如果有则访问,否则产生报错。

                  案例如下:

                  package main
                  import (
                  	"fmt"
                  )
                  // 基类
                  type Person struct {
                  	Name string
                  	Age  int
                  }
                  func (per Person) PrintInfo() {
                  	fmt.Printf("name = %s, age = %d\n", per.Name, per.Age)
                  }
                  // 派生类
                  type Student struct {
                  	Person
                  	StudentId int
                  }
                  func (stu Student) Study() {
                  	fmt.Printf("student %d is studying...\n", stu.StudentId)
                  }
                  func (stu Student) PrintInfo() {
                  	fmt.Printf("name = %s, age = %d, id = %d\n", stu.Name, stu.Age, stu.StudentId)
                  }
                  func main() {
                  	var stu = Student{Person{"Alice", 12}, 100}
                  	fmt.Printf("name = %s\n", stu.Name) // name = Alice
                  	stu.PrintInfo()                     // name = Alice, age = 12, id = 100
                  }
                  

                  代码说明:

                  • 在访问字段时,由于Student结构体中没有Name字段,因此Student变量.Name访问的是嵌套的匿名结构体Person中的Name字段。
                  • 在访问方法时,由于Student结构体有PrintInfo方法,因此Student变量.PrintInfo()访问的是Student结构体的PrintInfo方法,而不是Person结构体的PrintInfo方法。

                    多继承

                    多继承

                    多继承指的是一个结构体中嵌套了多个匿名结构体,如果嵌套的多个匿名结构体中有相同的字段名或方法名,那么在访问时需要通过匿名结构体的类型名进行指明访问。如下:

                    package main
                    import (
                    	"fmt"
                    )
                    type Cat struct {
                    	Name string
                    	Age  int
                    }
                    type Dog struct {
                    	Name string
                    	Age  int
                    }
                    // 多继承
                    type Pet struct {
                    	Cat
                    	Dog
                    }
                    func main() {
                    	var pet = Pet{
                    		Cat: Cat{
                    			Name: "小猫",
                    			Age:  2,
                    		},
                    		Dog: Dog{
                    			Name: "小狗",
                    			Age:  3,
                    		},
                    	}
                    	fmt.Printf("cat name = %s\n", pet.Cat.Name) // cat name = 小猫
                    	fmt.Printf("dog name = %s\n", pet.Dog.Name) // dog name = 小狗
                    }
                    

    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]