Go là sự pha trộn kỳ lạ giữa các ý tưởng cũ và ý tưởng mới. Nó có một cách tiếp cận hết sức mới mẻ, không sợ vứt bỏ những quan niệm đã được thiết lập về cách làm việc. Nhiều người thậm trí không nghĩ rằng Go là một ngôn ngữ lập trình hướng đối tượng.
Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu về tính đóng gói và đa hình trong ngôn ngữ lập trình Go, 2 khái niệm cực kỳ cơ bản trong ngôn ngữ lập trình hướng đối tượng.
Tính đóng gói
Tính đóng gói cung cấp nhiều hỗ trợ khác nhau cho khả năng đóng gói và giữ cho dữ liệu riêng tư. Nhưng bạn mong muốn có khả năng kiểm soát được việc truy cập. Thông thường, thậm trí nếu bạn có dữ liệu riêng trong các package, bạn có lẽ không muốn ẩn nó hoàn toàn. Nghĩa là chỉ mong muốn cho phép sử dụng dữ liệu theo cách mà chúng ta định nghĩa, sử dụng các phương thức và hàm của bạn.
Go đóng gói mọi thứ ở cấp độ package. Tên được bắt đầu bằng ký tự viết thường (private) chỉ có thể nhìn thấy bên trong package. Bạn có thể ẩn mọi thứ trong một gói riêng và chỉ đưa ra các kiểu cụ thể, interface, và các hàm factory.
- Ký tự đầu tiên viết hoa: Public
- Ký tự đầu tiên viết thường: Private
Private chỉ có thể truy cập bên trong package, trong khi đó, public hiển thị cả bên trong lẫn bên ngoài package.
Tên package được khai báo ở đầu mỗi file và phải giống với tên thư mục của nó trong hệ thống file. Bạn cũng có thể có nhiều file tập hợp trong một package đơn.
Hãy cùng xem ví dụ sau đây
package employee import "fmt" type Employee struct { // public id int name string salary float32 } func (self *Employee) raiseSalary(bonus float32) { // private self.salary += bonus } func (self Employee) format() string { // private return fmt.Sprintf("Id: %d, Name: %s, Salary: %.2f", self.id, self.name, self.salary) } func (self Employee) Greeting() { // public fmt.Sprintf("Hello: %s", self.name) }
Tính đa hình
Đa hình có nghĩa là có nhiều hình thức. Hay nói theo cách khác, chúng ta có thể coi tính đa hình như là khả năng một thông báo được hiển thị nhiều hơn một hình thức. Hay theo thuật ngữ của tính đa hình nghĩa là cùng một tên phương thức đang được sử dụng cho các kiểu khác nhau. Ví dụ như một phụ nữ cùng một thời điểm có thể có những đặc tính khác nhau, như là một người mẹ, một người vợ, một nhân viên… Do đó cùng một người có những hành vi khác nhau dựa trên ngữ cảnh cụ thể. Cái này được gọi là tính đa hình
Trong Go chúng ta không thể thực hiện tính đa hình sử dụng class bởi vì ngôn ngữ Go không hỗ trợ class, nhưng chúng ta có thể thực hiện bằng cách sử dụng interface. Như chúng ta đã biết interface được thực thi ngầm trong Go. Nên khi chúng ta tạo interface và mong muốn các kiểu khác thực thi, thì những kiểu này sử dụng interface đó với sự hỗ trợ của các phương thức được định nghĩa trong interface mà không cần biết kiểu. Trong một interface, biến của một kiểu interface có thể chứa bất kỳ giá trị nào mà thực thi interface. Tính chất này giúp cho interface thực hiện tính đa hình trong ngôn ngữ Go.
Hãy cùng xem ví dụ sau đây
// Chương trình Go mô tả tính đa hình sử dụng interface package main import "fmt" // Interface type employee interface { develop() int name() string } // Structure 1 type team1 struct { totalapp_1 int name_1 string } // các phương thức của employee interface được // thực thi bởi team1 structure func (t1 team1) develop() int { return t1.totalapp_1 } func (t1 team1) name() string { return t1.name_1 } // Structure 2 type team2 struct { totalapp_2 int name_2 string } // các phương thức của employee interface được // thực thi bởi team2 structure func (t2 team2) develop() int { return t2.totalapp_2 } func (t2 team2) name() string { return t2.name_2 } func finaldevelop(i []employee) { totalproject := 0 for _, ele := range i { fmt.Printf("\nProject environment = %s\n ", ele.name()) fmt.Printf("Total number of project %d\n ", ele.develop()) totalproject += ele.develop() } fmt.Printf("\nTotal projects completed by "+ "the company = %d", totalproject) } // Main function func main() { res1 := team1{totalapp_1: 20, name_1: "Android"} res2 := team2{totalapp_2: 35, name_2: "IOS"} final := []employee{res1, res2} finaldevelop(final) }
Trong ví dụ bên trên, chúng ta có một interface tên là employee
. Trong interface này chứa 2 phương thức develop(
) và name()
. Ở đây phương thức develop()
trả về tổng số dự án và phương thức name()
trả về tên môi trường phát triển.
Hiện tại chúng ta có 2 struct là team1 và team2, cả 2 struct đều chứa 2 trường totalapp_1 int
, name_1 string
, totalapp_2 int
, và name_2 string
. Những struct này thực thi các phương thức của employee interface.
Sau đó chúng ta tạo một hàm với tên là finaldevelop()
trả về tổng số dự án được phát triển bởi công ty. Nó nhận một slice employee interface như một tham số và tính toán tổng số dự án phát triển bởi công ty bằng cách lặp slice và gọi phương thức develop()
trên mỗi phần tử của nó. Nó cũng hiển thị môi trường của dự án bằng cách gọi phương thức name()
. Tuỳ theo kiểu cụ thể của employee
interface, phương thức name()
và develop()
khác nhau sẽ được gọi. Nên nó có được tính đa hình trong hàm finaldevelop()
Kết quả
Project environment = Android Total number of project 20 Project environment = IOS Total number of project 35 Total projects completed by the company = 55