速通Golang的面向对象编程

109 阅读2分钟

对象的属性行为

封装属性与行为

// 封装属性
type Employee struct {
   Id   string
   Name string
   Age  int
}

// 封装行为
func (e Employee) String1() string {
   return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}
func (e *Employee) String2() string {
   return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}

创建并初始化

func TestCreateEmployeeObj(t *testing.T) {
   //创建和初始化
   e := Employee{"0", "Bob", 20}
   e1 := Employee{Name: "Mike", Age: 30}
   e2 := new(Employee) // 返回的是指针
   e2.Id = "2"         //指定属性不能使用->
   e2.Age = 22
   e2.Name = "Rose"
   t.Log(e)              //{0 Bob 20}
   t.Log(e1)             //{ Mike 30}
   t.Log(e1.Id)          //空字符串,没有赋值的即为默认值
   t.Log(e2)             //&{2 Rose 22}
   t.Logf("e is %T", e)  //e is try_test.Employee,实体类型
   t.Logf("e is %T", e2) //e is *try_test.Employee,指针类型
}

行为的区别

image.png 测试如下,我们使用两个方法,并输出实体类的地址,看地址是否相同

// 封装行为
func (e Employee) String1() string {
   fmt.Printf("String1: Address is %x\n", unsafe.Pointer(&e.Name)) //输出地址
   return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}
func (e *Employee) String2() string {
   fmt.Printf("String2: Address is %x\n", unsafe.Pointer(&e.Name)) //输出地址
   return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}
func TestStructOperations(t *testing.T) {
   e := Employee{"0", "Bob", 20}
   fmt.Printf("Origin: Address is %x\n", unsafe.Pointer(&e.Name)) //输出地址
   t.Log(e.String1())
   t.Log(e.String2())
}

image.png 我们发现String2的地址和最初的地址是相同的,因此我们可用得知在封装行为的时候,如果使用的是指针类型,那么执行的对象就是原有对象,如果不是指针类型,那么会进行对象的复制,即执行的对象不是原有对象。推荐使用指针类型,减少对象赋值,节约资源。

接口

  • 接口为非侵入性,实现不依赖与接口定义
  • 所以接口的定义可用包含在接口使用者包内

接口定义

// 接口
type Programmer interface {
   WriteHelloWorld() string
}

接口实现

// 实现
type GoProgrammer struct {
}

func (gp *GoProgrammer) WriteHelloWorld() string {
   return "Hello World"
}

向上转型

func TestClient(t *testing.T) {
   var p Programmer = new(GoProgrammer)
   t.Log(p.WriteHelloWorld()) //Hello World
}

接口变量

image.png

自定义类型

问题来源 image.png 解决方法

image.png

扩展与复用

不具有继承关系

package try_test

import (
   "fmt"
   "testing"
)

// 定义父类
type Pet struct {
}

func (p *Pet) Speak() {
   fmt.Print("宠物: ")
}

func (p *Pet) SpeakTo(name string) {
   p.Speak()
   fmt.Println(name)
}

// 定义子类
type Dog struct {
   p *Pet
}

// 重写
func (d *Dog) Speak() {
   fmt.Print("狗: ")
}

// 复用
func (d *Dog) SpeakTo(name string) {
   d.p.SpeakTo(name)
}

func TestDog(t *testing.T) {
   dog := new(Dog)
   dog.SpeakTo("小花") //宠物: 小花
}

多态

  • 父类接口:Programmer
  • 子类实现:GoProgrammer、JavaProgrammer
type Code string

// 接口
type Programmer interface {
   WriteHelloWorld() Code
}

// 实现
type GoProgrammer struct {
}

func (gp *GoProgrammer) WriteHelloWorld() Code {
   return "Go: Hello World"
}

type JavaProgrammer struct {
}

func (p *JavaProgrammer) WriteHelloWorld() Code {
   return "Java: Hello World"
}

func WriteProgram(p Programmer) {
   fmt.Printf("%T %s\n", p, p.WriteHelloWorld())//输出类型与返回值
}
func TestClient(t *testing.T) {
   var p1 Programmer = new(GoProgrammer)
   var p2 Programmer = new(JavaProgrammer)
   WriteProgram(p1)
   WriteProgram(p2)
}

image.png

空接口与断言

  • 空接口可以表示任何类型
  • 可以通过断言将空接口转换为指定类型
func DoSomething(p interface{}) {
   //断言判断
   if i, ok := p.(int); ok {
      fmt.Println("Integer", i)
      return
   }
   if s, ok := p.(string); ok {
      fmt.Println("String", s)
      return
   }
   fmt.Println("Unknow type")
}

func TestEmptyInterfaceAssertion(t *testing.T) {
   DoSomething(10)
   DoSomething("10")
}

image.png

Switch结构优化

func DoSomething(p interface{}) {
   //断言判断
   switch v := p.(type) {
   case int:
      fmt.Println("Integer ", v)
   case string:
      fmt.Println("String ", v)
   default:
      fmt.Println("Unknow type")
   }
}

本文章参考来自Go语言从入门到实战