Is Go an object-oriented language?

Yes and no. Although Go has types and methods and allows an object

oriented style of programming, there is no type hierarchy. The concept

of “interface” in Go provides a different approach that we believe is

easy to use and in some ways more general.

Also, the lack of a type hierarchy makes “objects” in Go feel much more

lightweight than in languages such as C++ or Java.

行为的定义和实现

结构体定义:

type Employee struct {
    Id   string
    Name string
    Age  int
}

实例创建及初始化:

func TestCreateEmployee(t *testing.T) {
    e := Employee{"0", "Bob", 20}         // 分别把值放进去
    e1 := Employee{Name: "Mike", Age: 30} // 指定某个field的值
    e2 := new(Employee)                   // new关键字 去创建指向实例的指针 这里返回的引用/指针 相当于 e:=Employee{}
    e2.Id = "2"                           // 通过 example.filed 去赋值
    e2.Name = "Rose"
    e2.Age = 22
    t.Log(e)
    t.Log(e1)
    t.Log(e1.Id)
    t.Log(e2)
    t.Logf("e is %T", e)
    t.Logf("e2 is %T", e2)
    /** 运行结果:
    === RUN   TestCreateEmployee
        TestCreateEmployee: encap_test.go:18: {0 Bob 20}
        TestCreateEmployee: encap_test.go:19: { Mike 30}
        TestCreateEmployee: encap_test.go:20:
        TestCreateEmployee: encap_test.go:21: &{2 Rose 22}
        TestCreateEmployee: encap_test.go:22: e is test.Employee
        TestCreateEmployee: encap_test.go:23: e2 is *test.Employee
    --- PASS: TestCreateEmployee (0.00s)
     */
}

行为定义:

// 第一种定义方式在实例对应方法被调用时,实例的成员会进行值复制
func (e Employee) String() string {
    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}
    t.Log(e.String())
    /** 运行结果:
    === RUN   TestStructOperations
        TestStructOperations: encap_test.go:46: ID:0-Name:Bob-Age:20
    --- PASS: TestStructOperations (0.00s)
     */
}
// 通常情况下为了避免内存拷贝我们使用第二种定义方式
func (e *Employee) String() string {
    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}
    t.Log(e.String())
    /** 运行结果:
    === RUN   TestStructOperations
        TestStructOperations: encap_test.go:51: ID:0/Name:Bob/Age:20
    --- PASS: TestStructOperations (0.00s)
    */
}
在Go语言中不管通过指针访问还是通过实例访问,都是一样的

那么这两种定义没有区别吗??

func (e *Employee) String() string {
    fmt.Printf("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("Address is %x \n", unsafe.Pointer(&e.Name))
    t.Log(e.String())
    /** 运行结果:
    === RUN   TestStructOperations
    Address is c000060370
    Address is c000060370
        TestStructOperations: encap_test.go:54: ID:0/Name:Bob/Age:20
    --- PASS: TestStructOperations (0.00s)
    */
}

可以发现两个地址一致

func (e Employee) String() string {
    fmt.Printf("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("Address is %x \n", unsafe.Pointer(&e.Name))
    t.Log(e.String())
    /** 运行结果:
    === RUN   TestStructOperations
    Address is c000092370
    Address is c0000923a0
        TestStructOperations: encap_test.go:55: ID:0-Name:Bob-Age:20
    --- PASS: TestStructOperations (0.00s)
    */
}

这时候两个地址不一致,说明结构体的数据被复制了,会造成开销

Go语言的相关接口

Java的接口与依赖:

Go的 Duck Type 式接口实现:

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

type Programmer interface {
    WriteHelloWorld() string
}

type GoProgrammer struct {
}

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

func TestClient(t *testing.T) {
    var p Programmer
    p = new(GoProgrammer)
    t.Log(p.WriteHelloWorld())
    /** 运行结果:
    === RUN   TestClient
        TestClient: interface_test.go:19: Hello World
    --- PASS: TestClient (0.00s)
     */
}

接口变量:

自定义类型:

  • type IntConvertionFn func(n int) int
  • type MyPoint int

扩展和复用

复合:

  • Go不支持继承,可以通过复合的方式来复用

匿名类型嵌入:

它不是继承,如果我们把“内部 struct”看作父类,把“外部 struct” 看作子类,会发现如下问题:

  • 不支持子类替换
  • 子类并不是真正继承了父类的方法

    • 父类定义的方法无法访问子类的数据和方法
type Pet struct {
}

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

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

type Dog struct {
    p *Pet
}

func (d *Dog) Speak() {
    fmt.Print("Wang!")
}

func (d *Dog) SpeakTo(string string) {
    d.p.SpeakTo(string)
}

func TestDog(t *testing.T) {
    dog := new(Dog)
    dog.SpeakTo("Gao") // 没有打印 Wang! 需要改动 dog中SpeakTo方法
    /** 运行结果:
    === RUN   TestDog
    ...  Gao
    --- PASS: TestDog (0.00s)
     */
}

多态与空接口

多态:

type Code string // 自定义类型

type Programmer interface {
    WriteHelloWorld() Code
}

type GoProgrammer struct {
}

type PhpProgrammer struct {
}

func (g *GoProgrammer) WriteHelloWorld() Code {
    return "fmt.Println(\"Hello World\")"
}

func (p *PhpProgrammer) WriteHelloWorld() Code {
    return "echo \"Hello World\""
}

func writeFirstProgram(p Programmer) {
    fmt.Printf("%T %v\n", p, p.WriteHelloWorld())
}

func TestPolymorphism(t *testing.T) {
    goProg := new(GoProgrammer)
    phpProg := new(PhpProgrammer)
    writeFirstProgram(goProg)
    writeFirstProgram(phpProg)
    /** 运行结果
    === RUN   TestPolymorphism
    *test.GoProgrammer fmt.Println("Hello World")
    *test.PhpProgrammer echo "Hello World"
    --- PASS: TestPolymorphism (0.00s)
     */
}

空接口与断言:

  • 空接口可以表示任何类型
  • 通过断言来将空接口转换为制定类型

    v, ok := p.(int) // ok=true 时为转换成功

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")

    // 也可以通过switch来判断
    /*switch v := p.(type) {
    case int:
        fmt.Println("Integer", v)
    case string:
        fmt.Println("String", v)
    default:
        fmt.Println("Unknow Type")
    }*/
}

func TestEmptyInterfaceAssertion(t *testing.T) {
    DoSomething(10)
    DoSomething("gaobinzhan")
    /** 运行结果
    === RUN   TestEmptyInterfaceAssertion
    Integer 10
    String gaobinzhan
    --- PASS: TestEmptyInterfaceAssertion (0.00s)
    */
}

Go接口最佳实践:

最后修改:2020 年 05 月 18 日 11 : 15 PM