Ngôn ngữ Go cung cấp một kiểu dữ liệu được gọi là Interface được sử dụng để diễn tả tập hợp các phương thức đăng ký. Kiểu dữ liệu Struct implement những phương thức này để giúp cho các phương thức đăng ký bên trong Interface được định nghĩa.
Ví dụ WashingMachine
là một interface với các phương thức đăng ký Cleaning()
và Drying()
. Bất cứ kiểu nào mà cung cấp các định nghĩa cho Cleaning()
và Drying()
được hiểu là implement của WashingMachine
interface.
Khai báo và thực thi một interface
Để implement một Interface, bạn chỉ cần implement tất cả các phương thức được khai báo trong Interface.
Không giống như các ngôn ngữ khác như là Java, chúng ta không cần chỉ định rõ ràng một kiểu implement cho một Interface sử dụng từ khoá kiểu như là implements
.
Đây là 2 kiểu Struct implement cho Shape
interface.
type Rectangle struct { Length, Width float64 } func (r Rectangle) Area() float64 { return r.Length * r.Width } func (r Rectangle) Perimeter() float64 { return 2 * (r.Length + r.Width) }
type Circle struct { Radius float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius } func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius } func (c Circle) Diameter() float64 { return 2 * c.Radius }
Sử dụng một kiểu interface với các giá trị cụ thể
Bản thân một Interface là không hữu ích trừ khi chúng ta sử dụng chúng với một kiểu cụ thể implement toàn bộ phương thức của nó.
Chúng ta hãy cùng xem cách một Inteface có thể được sử dụng với giá trị cụ thể
1> Một kiểu Interface có thể nắm giữ bất kỳ giá trị nào implement tất cả phương thức của nó
package main import ( "fmt" "math" ) func main() { var s Shape = Circle{5.0} fmt.Printf("Shape Type = %T, Shape Value = %v\n", s, s) fmt.Printf("Area = %f, Perimeter = %f\n\n", s.Area(), s.Perimeter()) var s1 Shape = Rectangle{4.0, 6.0} fmt.Printf("Shape Type = %T, Shape Value = %v\n", s1, s1) fmt.Printf("Area = %f, Perimeter = %f\n", s1.Area(), s1.Perimeter()) }
Kết quả
Shape Type = main.Circle, Shape Value = {5} Area = 78.539816, Perimeter = 31.415927 Shape Type = main.Rectangle, Shape Value = {4 6} Area = 24.000000, Perimeter = 20.000000
2> Sử dụng kiểu Interface như tham số truyền vào của hàm
import ( "fmt" ) // Hàm chung để tính toán tổng area của các kiểu khác nhau của shapes func CalculateTotalArea(shapes ...Shape) float64 { totalArea := 0.0 for _, s := range shapes { totalArea += s.Area() } return totalArea } func main() { totalArea := CalculateTotalArea(Circle{2}, Rectangle{4, 5}, Circle{10}) fmt.Println("Total area = ", totalArea) }
Kết quả
Total area = 346.7256359733385
3> Sử dụng kiểu interface như một trường
package main import ( "fmt" ) // Kiểu Interface có thể được sử dụng như một trường type MyDrawing struct { shapes []Shape bgColor string fgColor string } func (drawing MyDrawing) Area() float64 { totalArea := 0.0 for _, s := range drawing.shapes { totalArea += s.Area() } return totalArea } func main() { drawing := MyDrawing{ shapes: []Shape{ Circle{2}, Rectangle{3, 5}, Rectangle{4, 7}, }, bgColor: "red", fgColor: "white", } fmt.Println("Drawing", drawing) fmt.Println("Drawing Area = ", drawing.Area()) }
Kết quả
Drawing {[{2} {3 5} {4 7}] red white} Drawing Area = 55.56637061435917
Các giá trị Interface: Một kiểu Interface làm việc với các giá trị cụ thể như thế nào?
Một gía trị Interface có thể được hiểu như là một Tuple bao gồm một giá trị và một kiểu cụ thể
// interface (value, type)
Hãy cùng xem ví dụ sau để hiểu hơn
package main import ( "fmt" ) func main() { var s Shape s = Circle{5} fmt.Printf("(%v, %T)\n", s, s) fmt.Printf("Shape area = %v\n", s.Area()) s = Rectangle{4, 7} fmt.Printf("(%v, %T)\n", s, s) fmt.Printf("Shape area = %v\n", s.Area()) }
Kết quả
({5}, main.Circle) Shape area = 78.53981633974483 ({4 7}, main.Rectangle) Shape area = 28
Kiểm tra kết quả của chương trình bên trên và chú ý cách biến s
chứa các thông tin về giá trị cũng như kiểu của Shape
mà nó đang được gán.