Series Golang cơ bản (Phần 19: Interfaces – II)

  • 04/12/2018
  • Bởi
  • trong Go

Chào mừng bạn đến với bài hướng dẫn 19 trong loạt bài hướng dẫn Golang. Đây là phần thứ hai trong hướng dẫn Interfaces 2 phần của chúng tôi. Trong trường hợp bạn bỏ lỡ phần đầu tiên, bạn có thể đọc nó ở đây.

Triển khai Interfaces sử dụng con trỏ và giá trị
Tất cả các Interfaces ví dụ mà chúng ta đã thảo luận trong phần 1 đã được thực hiện bằng cách sử dụng giá trị. Nó cũng có thể thực hiện các giao diện bằng cách sử dụng con trỏ. Có một sự tinh tế cần lưu ý trong khi thực hiện các Interfaces bằng cách sử dụng con trỏ. Hãy tìm hiểu rằng nó trong chương trình sau đây:

package main

import "fmt"

type Describer interface {  
    Describe()
}
type Person struct {  
    name string
    age  int
}

func (p Person) Describe() { //implemented using value receiver  
    fmt.Printf("%s is %d years old\n", p.name, p.age)
}

type Address struct {  
    state   string
    country string
}

func (a *Address) Describe() { //implemented using pointer receiver  
    fmt.Printf("State %s Country %s", a.state, a.country)
}

func main() {  
    var d1 Describer
    p1 := Person{"Sam", 25}
    d1 = p1
    d1.Describe()
    p2 := Person{"James", 32}
    d1 = &p2
    d1.Describe()

    var d2 Describer
    a := Address{"Washington", "USA"}

    /* compilation error if the following line is
       uncommented
       cannot use a (type Address) as type Describer
       in assignment: Address does not implement
       Describer (Describe method has pointer
       receiver)
    */
    //d2 = a

    d2 = &a //This works since Describer interface
    //is implemented by Address pointer in line 22
    d2.Describe()

}

Run in playground

Trong chương trình trên, cấu trúc Person triển khai Interfaces Describer bằng cách sử dụng giá trị.

Như chúng ta đã học được trong quá trình thảo luận về các phương thức, các phương thức với bộ nhận giá trị chấp nhận cả con trỏ và giá trị.

p1 là một giá trị của kiểu Person và nó được gán cho d1, tương tự, d1 được gán cho &p2.

Kết quả:

Sam is 25 years old  
James is 32 years old  
State Washington Country USA  

Triển khai nhiều Interfaces

package main

import (  
    "fmt"
)

type SalaryCalculator interface {  
    DisplaySalary()
}

type LeaveCalculator interface {  
    CalculateLeavesLeft() int
}

type Employee struct {  
    firstName string
    lastName string
    basicPay int
    pf int
    totalLeaves int
    leavesTaken int
}

func (e Employee) DisplaySalary() {  
    fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}

func (e Employee) CalculateLeavesLeft() int {  
    return e.totalLeaves - e.leavesTaken
}

func main() {  
    e := Employee {
        firstName: "Naveen",
        lastName: "Ramanathan",
        basicPay: 5000,
        pf: 200,
        totalLeaves: 30,
        leavesTaken: 5,
    }
    var s SalaryCalculator = e
    s.DisplaySalary()
    var l LeaveCalculator = e
    fmt.Println("\nLeaves left =", l.CalculateLeavesLeft())
}

Run in playground

Chương trình trên có hai Interfaces là SalaryCalculator và LeaveCalculator được khai báo.

Cấu trúc Employee được định nghĩa cung cấp các triển khai cho phương thức DisplaySalary của Interfaces SalaryCalculator và phương thức CalculateLeavesLeft của Interfaces LeaveCalculator.

Chúng ta gán e cho một biến kiểu Interfaces SalaryCalculator, sau đó gán cùng một biến e cho một biến kiểu LeaveCalculator. Điều này là có thể vì e của loại Employee thực hiện cả hai Interfaces SalaryCalculator và LeaveCalculator.

Kết quả:

Naveen Ramanathan has salary $5200
Leaves left = 25

Nhúng  Interfaces

package main

import (  
    "fmt"
)

type SalaryCalculator interface {  
    DisplaySalary()
}

type LeaveCalculator interface {  
    CalculateLeavesLeft() int
}

type EmployeeOperations interface {  
    SalaryCalculator
    LeaveCalculator
}

type Employee struct {  
    firstName string
    lastName string
    basicPay int
    pf int
    totalLeaves int
    leavesTaken int
}

func (e Employee) DisplaySalary() {  
    fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}

func (e Employee) CalculateLeavesLeft() int {  
    return e.totalLeaves - e.leavesTaken
}

func main() {  
    e := Employee {
        firstName: "Naveen",
        lastName: "Ramanathan",
        basicPay: 5000,
        pf: 200,
        totalLeaves: 30,
        leavesTaken: 5,
    }
    var empOp EmployeeOperations = e
    empOp.DisplaySalary()
    fmt.Println("\nLeaves left =", empOp.CalculateLeavesLeft())
}

Run in playground

Interfaces EmployeeOperations của chương trình trên được tạo ra bằng cách nhúng các Interfaces SalaryCalculator và LeaveCalculator.

Bất kỳ loại nào được cho là để triển khai Interfaces EmployeeOperations nếu nó cung cấp các định nghĩa phương thức cho các phương thức có trong cả hai Interfaces SalaryCalculator và LeaveCalculator.

Employee struct triển khai thực hiện giao diện EmployeeOperations vì nó cung cấp định nghĩa cho cả hai phương thức DisplaySalary và CalculateLeavesLeft.

Biến e của kiểu Employee được gán cho empOp của kiểu EmployeeOperations. Tiếp theo, các phương thức DisplaySalary () và CalculateLeavesLeft () được gọi trên empOp.

Kết quả:

Naveen Ramanathan has salary $5200  
Leaves left = 25 

Giá trị 0 của Interfaces
Giá trị bằng không của một Interfaces là  nil. Một Interfaces nil có cả giá trị cơ bản và cụ thể là nil.

package main

import "fmt"

type Describer interface {  
    Describe()
}

func main() {  
    var d1 Describer
    if d1 == nil {
        fmt.Printf("d1 is nil and has type %T value %v\n", d1, d1)
    }
}

Run in playground

d1 trong chương trình trên là 0 và chương trình này sẽ xuất kết quả:

d1 is nil and has type <nil> value <nil>  

Nếu bạn cố gắng thử phương thức nil trong Interfaces, chương trình sẽ lỗi vì nil trong Interfaces không có giá trị cơ bản cũng như một loại cụ thể.

package main

type Describer interface {  
    Describe()
}

func main() {  
    var d1 Describer
    d1.Describe()
}

Run in playground

Vì d1 trong chương trình là nil nên khi chạy sẽ báo lỗi như sau:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x44e9ef]

Đến đây chúng ta đã tìm hiểu xong về Interfaces, y vọng bạn sẽ thích và vui lòng để lại phản hồi ở phía dưới nhé!

Mời các bạn xem bài hướng dẫn tiếp theo: Phần 20 - Concurrency

Cấu trúc thư mục một project sử dụng Go-Micro Cấu trúc thư mục một project sử dụng Go-Micro Techmaster team Blog Home Giới thiệu Go Module trong Go v1.11, chào tạm biệt GOPATH! Giới thiệu Go Module trong Go v1.11, chào tạm biệt GOPATH! Tào Quỳnh