Go语言基础学习 (五) - 面向对象编程

301 阅读5分钟

前言

在极客上看了蔡超老师的Go语言课程 随手记下来的一些随笔,Go的基础应用及实例, 系列内容比较偏基础,推荐给想要入门Go语言开发者们阅读。

目录如下

Go语言基础学习 (一) - 变量 常量已经与其他语言的差异
Go语言基础学习 (二) -Go语言中的类型转与Go语言中的数组切片
Go语言基础学习 (三) - Go语言内的 Map声明使用与工厂模式
Go语言基础学习 (四) - Go语言函数简单介绍
Go语言基础学习 (五) - 面向对象编程
Go语言基础学习 (六) - 编写一个好的错误处理
Go语言基础学习 (七) - 包(package)
Go语言基础学习 (八) - 并发编程

  • 其实GO并不是一个纯面向对象编程语言。它没有提供类(class)这个关键字,只提供了结构体(struct)类型。java或者C# 里面,结构体(struct)是不能有成员函数的。然而,Go语言中的结构体(struct)可以有"成员函数"。方法可以被添加到结构体中,类似于一个类的实现。

1. 结构体定义

image-20220216112239974.png

2. 结构体初始化实例化的几种方法

  • 实例化去赋值

    第一种:分别把每个参数的值放在{}内

       type Employee struct {
           Id    string
           Name  string
           Age   string
       }
    
       e := Employee{"0","Bob",20}
    
    

    第二种:与第一种类似,指定给每一个key赋值

    
        type Employee struct {
            Id    string
            Name  string
            Age   string
        }
    
        e := Employee{Name:"Mike",Age",20}
    

    第三种: 创建一个指向实例的指针,然后通过 .Id 来单独赋值

      type Employee struct {
           Id    string
           Name  string
           Age   string
       }
    
      a := new(Employee)
      a.Id = 1
      a.Name = "XiaoMing"
      
     
     
    

3. 使用行为(方法)定义

image-20220216144247864.png

  • 定义
     func (e Employee) String() string  {
                 return fmt.Sprintf("ID:%s/Name:%s/Age:%d",e.Id,e.name,e.Age)
     }
    
     func TestObj( t *testing.T) {
         e := Employee{1,"zhangsan",1}
         t.Log(e.String())  // ID:1/Name:zhangsan/Age:1
     }	
    

    使用指针调用

         func (e *Employee) String() string  {
             return fmt.Sprintf("ID:%s/Name:%s/Age:%d",e.Id,e.name,e.Age)
         }
    
         func TestObj( t *testing.T) {
             e := &Employee{1,"zhangsan",1}
             t.Log(e.String())  // ID:1/Name:zhangsan/Age:1
         }
    

    结果是相同的 两种方法都可以使用,而两种方法不一样点就是实例的存储地址不同
    第一种方法 在实例的调用中会进行复制,会有内存的消耗
    第二种方法 不会内存拷贝,减少开销

4. Go的接口相关

image-20220216155923294.png

  • 接口与依赖

    image-20220216151230936.png 简单代码实例

     type Programmer interface {   // 接口定义
      WirteHelloWorld() string   //  想要实现的方法写这里
     }
    
     type GoProgrammer struct {  // 接口的实现数据实例
             name string
     }
    
     func (g * GoProgrammer) WirteHelloWorld()string  {  // 使用指向类型的指针初始化实例
             g.name = "Hello World"
             return g.name
     }
    
     func TestClient(t *testing.T)  {
             var p Programmer
             p = new(GoProgrammer)
             t.Log(p.WirteHelloWorld())   // Hello World
     }
    
  • 使用interface 简单实现一个签名工具的方法
      package sign_test
    
      import (
              "fmt"
              "testing"
      )
    
      type SignFunc interface {
              VerifySign(sign string,key string) bool
              GetSignStr(param string,key string) string
      }
    
      type SignData struct {
              id int
              name string
              sex  int
              time int
              key  string
      }
    
      func (s * SignData) VerifySign(sign string,key string) bool {
                s.key = key
                // ...... 过程省略......
                return true
      }
    
      func (s * SignData) GetSignStr(param string,key string) string {
                      // 过程省略.........
               return  "12312312312"
      }
    
      func TestSign(t * testing.T)  {
               var s  SignFunc
               s = new(SignData)
               // 验证签名
               signRet := s.VerifySign("1231231231","dasdasdasdas")
               signStr := s.GetSignStr("12313123123131321","123131313213200")
               fmt.Println("签名验证结果:",signRet)
               fmt.Println("签名生成结果:",signStr)
      }
    
  • 自定义功能(第四节内的装饰性函数进一步处理优化
        // 未优化的函数 较为繁琐 
        func timeSpent(inner func(op int) int )  func(op int)int  {
            return func(n int) int {
                    start := time.Now()
                    ret   := inner(n)
                    fmt.Println("time spent:",time.Since(start).Seconds())
                    return ret
            }
        }
    
     // 优化后的
    type intConv func(op int) int 
    
    func timeSpent(inner IntConv )  IntConv  {
            return func(n int) int {
                    start := time.Now()
                    ret   := inner(n)
                    fmt.Println("time spent:",time.Since(start).Seconds())
                    return ret
            }
    }
    
  • 扩展与复用

    首先看一段PHP的子类继承扩展复用代码

    
     class Pet {
       public function speak(){
         var_dump('....');
       }
     
       public function speakTwo(string $host){
         $this->speak();
         var_dump($host);
       }
     }	
     
     class Dog extends Pet{
     
       public function speak()
       {
         var_dump('wang!');
       }
     
     }
     
     class Test {
       public function testSubClassAccess(string $host)
       {
         $dog = new Dog();
         $dog->speakTwo($host);
       }
     }
     
     $test =  new Test();
     
     $test->testSubClassAccess('test!'); // wang! test! 
    

    创建Pet类,写入两个方法,在创建Dog类,并声明重载speak方法,在调用子类,通过子类调用父类speakTwo方法是,父类会重写speak方法。

    但是go是没有继承的,可以使用struct结构体来模拟类

       type Pet struct {
               // 创造一个Pet的结构体 并且声明两个方法
       }
    
       func (p * Pet) Speak()  {   
           fmt.Println("...")
       }
    
       func (p * Pet) SpeakTwo(host string) {
            p.Speak()
            fmt.Println(host)
       }
    
       type Dog struct {  // 创建一个Dog的结构体,声明复用同样的方法
           p *Pet   // 直接指向Pet结构,可以在Dog结构下使用Pet的方法
       }
    
       func (d * Dog) Speak()  {
           d.p.Speak()  // 通过小数点调用p的方法
       }
    
       func (d *Dog) SpeakTwo(host string)  {
           d.p.SpeakTwo(host) // 通过小数点调用p的方法
       }
    
    
       func TestDog(t *testing.T)  {
           dog := new(Dog)
           dog.SpeakTwo("test!")
       }
    

    打印结果
    ...
    test!

    从上面结果看来 在Dog结构指向Pet结构体时,Dog是可以使用.来调用Pet内的方法,很像面向对象内的子类继承父类方法,但是在go里面是不支持子类继承重写的,Dog指向了Pet结构体 只代表Dog结构体可以调用Pet结构体内的方法

    试一下子类继承重写

        type Pet struct {
    
       }
    
       func (p * Pet) Speak()  {
               fmt.Println("...")
       }
    
       func (p * Pet) SpeakTwo(host string) {
                p.Speak()
                fmt.Println("   ",host)
       }
    
       type Dog struct {
               Pet 
       }
    
       func (d * Dog) Speak()  {  // 在此处尝试重写Pet结构内的方法
               fmt.Println("重写加载成功!!")
       }
    
    
       func TestDog(t *testing.T)  {
               dog := new(Dog)
               dog.SpeakTwo("test!")
       }
    
     ...
     test!
    

    结果还是同上面一样,所以Go语言内是不支持子类继承的方法复写与重定义

  • 多态性
        type code string   // 定义 code 为string类型 
        type Programmer interface {  // 创建interface
             WriteHelloWorld() code 
        }
        type GoProgrammer struct {   // 创建一个结构体
    
        }
        // 创建interface 下方法 使用GoProgrammer结构体来实现
        func (p *GoProgrammer) WriteHelloWorld() code  { 
        return "fmt.Println(\"Hello World!\")"
        }
        type JavaProgrammer struct {
    
        }
    
        func (p *JavaProgrammer) WriteHelloWorld() code  {
                 return "System.out.Println(\"Hello World!\")"
        }
    
        // 新增方法,传入interface类型
        func writeFirstProgrammer(p Programmer)  {
                 fmt.Printf("%T %v\n",p,p.WriteHelloWorld())  // 输入传入进来的inerface下指定的结构体方法
        }
    
        func TestPolymorphism(t *testing.T)  {
          goProg := new(GoProgrammer)
          javaProg := new(JavaProgrammer)
          writeFirstProgrammer(goProg) // 调用方法 传入对应的结构体来调用对应的interface方法
          writeFirstProgrammer(javaProg)
        }
    
        *interface_test.GoProgrammer fmt.Println("Hello World!")
        *interface_test.JavaProgrammer System.out.Println("Hello World!"
  • 空接口与断言

    image-20220222173518390.png

    第一个参数为返回值,第二个参数为bool,以第二个参数来判定是否转换成功。 使用断言来判断一个方法接受参数的类型

        func DoSomething(p interface{}) {
            if i, ok := p.(int); ok {
                    fmt.Println("Int", i)
                            return
                    }
                    if i, ok := p.(string); ok {
                            fmt.Println("String", i)
                            return
                    }
                    fmt.Println("Unknow Type")
            }
    
            func TestEmptyInterfaceAssertion(t *testing.T) {
                    DoSomething(1)
                    DoSomething("1")
                    DoSomething(true)
            }
    
        Int 1
     String 1
     Unknow Type