Nếu C ++ và Java là về phân cấp kiểu và phân loại các loại, thì Go là về thành phần.
Nếu C ++ và Java là về phân cấp kiểu và phân loại các loại, thì Go là về thành phần.
Câu trả lời:
Anh ta có nghĩa là nơi bạn sử dụng thứ gì đó theo thứ tự:
class A : public B {};
trong một cái gì đó như Java hoặc C ++, trong Go bạn sẽ sử dụng (một cái gì đó tương đương):
class A {
B b;
};
Vâng, điều này cung cấp khả năng giống như thừa kế. Hãy mở rộng ví dụ trên một chút:
struct B {
int foo() {}
};
struct A {
B b;
};
A a;
a.foo(); // not allowed in C++ or Java, but allowed in Go.
Tuy nhiên, để làm điều này, bạn sử dụng một cú pháp không được phép trong C ++ hoặc Java - bạn để đối tượng được nhúng mà không có tên của chính nó, vì vậy nó giống như:
struct A {
B;
};
Câu hỏi / vấn đề này là loại tương tự như câu hỏi này .
Trong Go, bạn không thực sự có OOP.
Nếu bạn muốn "chuyên môn hóa" một đối tượng, bạn thực hiện nó bằng cách nhúng, đó là một thành phần, nhưng với một số tính năng làm cho nó tương tự một phần với sự kế thừa. Bạn làm như thế này:
type ConnexionMysql struct {
*sql.DB
}
Trong mẫu này, ConnexionMysql là một loại chuyên môn của * sql.DB và bạn có thể gọi ConnexionMysql các hàm được định nghĩa trên * sql.DB:
type BaseMysql struct {
user string
password string
database string
}
func (store *BaseMysql) DB() (ConnexionMysql, error) {
db, err := sql.Open("mymysql", store.database+"/"+store.user+"/"+store.password)
return ConnexionMysql{db}, err
}
func (con ConnexionMysql) EtatBraldun(idBraldun uint) (*EtatBraldun, error) {
row := con.QueryRow("select pv, pvmax, pa, tour, dla, faim from compte where id=?", idBraldun)
// stuff
return nil, err
}
// somewhere else:
con, err := ms.bd.DB()
defer con.Close()
// ...
somethings, err = con.EtatBraldun(id)
Vì vậy, ngay từ cái nhìn đầu tiên, bạn có thể nghĩ rằng thành phần này là công cụ để thực hiện phân loại thông thường của bạn.
Nhưng
nếu một hàm được định nghĩa trên * sql.DB gọi các hàm khác được định nghĩa trên * sql.DB, thì nó sẽ không gọi các hàm được định nghĩa lại trên ConnexionMysql ngay cả khi chúng tồn tại.
Với sự kế thừa cổ điển, bạn thường làm một cái gì đó như thế này:
func (db *sql.DB) doComplexThing() {
db.doSimpleThing()
db.doAnotherSimpleThing()
}
func (db *sql.DB) doSimpleThing() {
// standard implementation, that we expect to override
}
Đó là, bạn xác định doComplexThing
trên siêu hạng là một tổ chức theo các cuộc gọi của các chuyên ngành.
Nhưng trong Go, điều này sẽ không gọi chức năng chuyên biệt mà là chức năng "siêu lớp".
Vì vậy, nếu bạn muốn có một thuật toán cần gọi một số hàm được xác định trên * sql.DB nhưng được định nghĩa lại trên ConnexionMyQuery (hoặc các chuyên ngành khác), bạn không thể định nghĩa thuật toán này là hàm của * sql.DB nhưng phải xác định nó ở nơi khác và chức năng này sẽ chỉ soạn các cuộc gọi đến chuyên môn được cung cấp.
Bạn có thể làm điều đó như thế này bằng các giao diện:
type interface SimpleThingDoer {
doSimpleThing()
doAnotherSimpleThing()
}
func doComplexThing(db SimpleThingDoer) {
db.doSimpleThing()
db.doAnotherSimpleThing()
}
func (db *sql.DB) doSimpleThing() {
// standard implementation, that we expect to override
}
func (db ConnexionMySQL) doSimpleThing() {
// other implemenation
}
Điều này khá khác biệt so với việc ghi đè cổ điển của hệ thống phân cấp lớp.
Đặc biệt, rõ ràng bạn không thể trực tiếp có một cấp thứ ba kế thừa việc thực hiện chức năng từ cấp thứ hai.
Trong thực tế, bạn sẽ kết thúc bằng cách sử dụng hầu hết các giao diện (trực giao) và để chức năng soạn thảo các cuộc gọi trên một triển khai được cung cấp thay vì có "siêu lớp" thực hiện các cuộc gọi đó.
Theo kinh nghiệm của tôi, điều này dẫn đến sự thiếu vắng thực tế của hệ thống phân cấp sâu hơn một cấp.
Thông thường, trong các ngôn ngữ khác, bạn có phản xạ, khi bạn thấy rằng khái niệm A là một chuyên môn của khái niệm B, để xác định thực tế này bằng cách tạo một lớp B và một lớp A là một lớp con của B. Thay vì tạo ra của bạn chương trình xung quanh dữ liệu của bạn, bạn dành thời gian tái tạo phân loại đối tượng trong mã của mình, theo nguyên tắc đây là thực tế.
Trong Go bạn không thể xác định một thuật toán chung và chuyên môn hóa nó. Bạn phải xác định một thuật toán chung và đảm bảo nó chung và hoạt động với các cài đặt giao diện được cung cấp.
Đã bị kinh hoàng bởi sự phức tạp ngày càng tăng của một số cây phân cấp mà các lập trình viên đã tạo ra các bản hack phức tạp để cố gắng đáp ứng một thuật toán mà logic của nó cuối cùng bao hàm tất cả các cấp, tôi nói rằng tôi hài lòng với logic Go đơn giản hơn, ngay cả khi nó buộc bạn nghĩ thay vì chỉ thống nhất các khái niệm về mô hình ứng dụng của bạn.