ถ้า C ++ และ Java เกี่ยวกับลำดับชั้นของประเภทและอนุกรมวิธานของประเภท Go เป็นเรื่องเกี่ยวกับองค์ประกอบ
ถ้า C ++ และ Java เกี่ยวกับลำดับชั้นของประเภทและอนุกรมวิธานของประเภท Go เป็นเรื่องเกี่ยวกับองค์ประกอบ
คำตอบ:
เขาหมายถึงสถานที่ที่คุณใช้บางอย่างตามลำดับ:
class A : public B {};
ในบางสิ่งบางอย่างเช่น Java หรือ C ++ ใน Go คุณจะใช้ (สิ่งที่เทียบเท่า):
class A {
B b;
};
ใช่สิ่งนี้ให้ความสามารถในการสืบทอด ลองขยายตัวอย่างด้านบนเล็กน้อย:
struct B {
int foo() {}
};
struct A {
B b;
};
A a;
a.foo(); // not allowed in C++ or Java, but allowed in Go.
อย่างไรก็ตามในการทำเช่นนี้คุณใช้ไวยากรณ์ที่ไม่ได้รับอนุญาตใน C ++ หรือ Java - คุณปล่อยให้วัตถุฝังตัวโดยไม่มีชื่อของมันเองดังนั้นมันจึงเป็นดังนี้:
struct A {
B;
};
คำถาม / ปัญหานี้เป็นชนิดของคล้ายกับคนนี้
ในระหว่างการเดินทางคุณไม่มี OOP จริงๆ
หากคุณต้องการ "เชี่ยวชาญ" วัตถุคุณทำได้โดยฝังซึ่งเป็นองค์ประกอบ แต่ด้วยสารพัดทำให้บางส่วนคล้ายกับการสืบทอด คุณทำเช่นนี้:
type ConnexionMysql struct {
*sql.DB
}
ในตัวอย่างนี้ ConnexionMysql เป็นประเภทเฉพาะของ * sql.DB และคุณสามารถเรียกใช้ ConnexionMysql ฟังก์ชั่นที่กำหนดไว้ใน * 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)
เมื่อแรกพบคุณอาจคิดว่าองค์ประกอบนี้เป็นเครื่องมือในการสร้างอนุกรมวิธานปกติของคุณ
แต่
หากฟังก์ชั่นที่กำหนดไว้ใน * sql.DB เรียกฟังก์ชั่นอื่น ๆ ที่กำหนดไว้ใน * sql.DB มันจะไม่เรียกฟังก์ชั่นที่กำหนดไว้ใน ConnexionMysql แม้ว่าพวกเขาจะมีอยู่
ด้วยการสืบทอดแบบคลาสสิกคุณมักจะทำสิ่งนี้:
func (db *sql.DB) doComplexThing() {
db.doSimpleThing()
db.doAnotherSimpleThing()
}
func (db *sql.DB) doSimpleThing() {
// standard implementation, that we expect to override
}
นั่นคือคุณกำหนดdoComplexThing
ในระดับซุปเปอร์เป็นองค์กรในสายของความเชี่ยวชาญ
แต่ใน Go สิ่งนี้จะไม่เรียกฟังก์ชันเฉพาะ แต่เป็นฟังก์ชัน "superclass"
ดังนั้นหากคุณต้องการอัลกอริทึมที่ต้องการเรียกใช้ฟังก์ชันบางอย่างที่กำหนดไว้ใน * sql.DB แต่นิยามใหม่ใน ConnexionMySQL (หรือความเชี่ยวชาญพิเศษอื่น ๆ ) คุณไม่สามารถกำหนดอัลกอริทึมนี้เป็นฟังก์ชันของ * sql.DB แต่ต้องกำหนดที่อื่น และฟังก์ชั่นนี้จะเขียนเฉพาะการโทรไปยังความเชี่ยวชาญเฉพาะที่มีให้
คุณสามารถทำได้โดยใช้อินเทอร์เฟซ:
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
}
นี่ค่อนข้างแตกต่างจากการเอาชนะแบบคลาสสิกของลำดับชั้น
โดยเฉพาะอย่างยิ่งคุณไม่สามารถมีระดับที่สามโดยตรงที่สืบทอดการใช้งานฟังก์ชั่นจากระดับที่สอง
ในทางปฏิบัติคุณจะสิ้นสุดการใช้อินเทอร์เฟซ (orthogonal) เป็นส่วนใหญ่และให้ฟังก์ชันเขียนการเรียกบนการใช้งานที่เตรียมไว้แทนที่จะใช้ "ซูเปอร์คลาส" ของการจัดระเบียบการโทรเหล่านั้น
จากประสบการณ์ของฉันสิ่งนี้นำไปสู่การขาดการปฏิบัติของลำดับชั้นลึกกว่าหนึ่งระดับ
บ่อยครั้งที่ภาษาอื่นคุณมีความสะท้อนเมื่อคุณเห็นว่าแนวคิด A เป็นความเชี่ยวชาญเฉพาะของแนวคิด B เพื่อทบทวนความจริงนี้โดยการสร้างคลาส B และคลาส A เป็นคลาสย่อยของ B แทนการสร้างของคุณ โปรแกรมรอบข้อมูลของคุณคุณใช้เวลาในการจำลองอนุกรมวิธานของวัตถุในรหัสของคุณบนหลักการที่ว่านี่คือความจริง
ใน Go คุณไม่สามารถกำหนดอัลกอริทึมทั่วไปและชำนาญได้ คุณต้องกำหนดอัลกอริทึมทั่วไปและให้แน่ใจว่าเป็นเรื่องทั่วไปและทำงานร่วมกับการใช้อินเทอร์เฟซที่ให้ไว้
ต้องตกใจกับความซับซ้อนที่เพิ่มขึ้นของต้นไม้ลำดับชั้นที่ผู้เขียนทำแฮ็กที่ซับซ้อนเพื่อพยายามหาอัลกอริทึมซึ่งในที่สุดตรรกะก็หมายถึงทุกระดับฉันจะบอกว่าฉันมีความสุขกับตรรกะ Go ที่ง่ายกว่าแม้ว่ามันจะบังคับ ให้คุณคิดแทนการปรับเปลี่ยนแนวคิดของโมเดลแอปพลิเคชันของคุณอีกครั้ง