From Less is Exponentially More
Jika C ++ dan Java adalah tentang hierarki tipe dan taksonomi tipe, Go adalah tentang komposisi.
From Less is Exponentially More
Jika C ++ dan Java adalah tentang hierarki tipe dan taksonomi tipe, Go adalah tentang komposisi.
Jawaban:
Maksudnya di mana Anda akan menggunakan sesuatu berdasarkan urutan:
class A : public B {};
di sesuatu seperti Java atau C ++, di Go Anda akan menggunakan (sesuatu yang setara dengan):
class A {
B b;
};
Ya, ini memberikan kemampuan seperti warisan. Mari kita sedikit memperluas contoh di atas:
struct B {
int foo() {}
};
struct A {
B b;
};
A a;
a.foo(); // not allowed in C++ or Java, but allowed in Go.
Untuk melakukannya, Anda menggunakan sintaks yang tidak diizinkan di C ++ atau Java - Anda meninggalkan objek yang disematkan tanpa nama sendiri, jadi lebih seperti:
struct A {
B;
};
Pertanyaan / masalah ini agak mirip dengan yang ini .
Di Go, Anda tidak benar-benar memiliki OOP.
Jika Anda ingin "mengkhususkan" suatu objek, Anda melakukannya dengan menanamkan, yang merupakan komposisi, tetapi dengan beberapa barang membuatnya sebagian mirip dengan warisan. Anda melakukannya seperti ini:
type ConnexionMysql struct {
*sql.DB
}
Dalam sampel ini, ConnexionMysql adalah jenis spesialisasi * sql.DB, dan Anda dapat memanggil ConnexionMysql fungsi yang didefinisikan pada * 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)
Jadi pada pandangan pertama Anda mungkin berpikir bahwa komposisi ini adalah alat untuk membuat taksonomi Anda yang biasa.
Tapi
jika suatu fungsi yang didefinisikan pada * sql.DB memanggil fungsi-fungsi lain yang didefinisikan pada * sql.DB, itu tidak akan memanggil fungsi-fungsi yang didefinisikan ulang pada ConnexionMysql bahkan jika mereka ada.
Dengan warisan klasik, Anda sering melakukan sesuatu seperti ini:
func (db *sql.DB) doComplexThing() {
db.doSimpleThing()
db.doAnotherSimpleThing()
}
func (db *sql.DB) doSimpleThing() {
// standard implementation, that we expect to override
}
Artinya, Anda mendefinisikan doComplexThing
kelas super sebagai organisasi berdasarkan panggilan spesialisasi.
Tetapi di Go, ini tidak akan memanggil fungsi khusus tetapi fungsi "superclass".
Jadi, jika Anda ingin memiliki algoritma yang perlu memanggil beberapa fungsi yang didefinisikan pada * sql.DB tetapi didefinisikan ulang pada ConnexionMySQL (atau spesialisasi lainnya), Anda tidak dapat mendefinisikan algoritma ini sebagai fungsi * sql.DB tetapi harus mendefinisikannya di tempat lain dan fungsi ini hanya akan menyusun panggilan ke spesialisasi yang disediakan.
Anda bisa melakukannya seperti ini menggunakan antarmuka:
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
}
Ini sangat berbeda dari pengesampingan klasik hierarki kelas.
Terutama, Anda jelas tidak dapat secara langsung memiliki tingkat ketiga yang mewarisi implementasi fungsi dari yang kedua.
Dalam praktiknya, Anda akan menggunakan sebagian besar antarmuka (ortogonal) dan membiarkan fungsi membuat panggilan pada implementasi yang disediakan alih-alih meminta "superclass" implementasi mengatur panggilan-panggilan tersebut.
Dalam pengalaman saya, ini mengarah pada tidak adanya hierarki praktis yang lebih dalam dari satu level.
Terlalu sering, dalam bahasa lain, Anda memiliki refleks, ketika Anda melihat bahwa konsep A adalah spesialisasi dari konsep B, untuk memverifikasi fakta ini dengan membuat kelas B dan kelas A sebagai subkelas B. Bukan membuat Anda program di sekitar data Anda, Anda menghabiskan waktu mereproduksi taksonomi objek dalam kode Anda, dengan prinsip bahwa ini adalah kenyataan.
Di Go, Anda tidak dapat menentukan algoritma umum dan mengkhususkannya. Anda harus menentukan algoritma umum dan memastikannya umum dan berfungsi dengan implementasi antarmuka yang disediakan.
Setelah merasa ngeri dengan semakin kompleksnya beberapa pohon hierarki di mana para pembuat kode membuat peretasan yang rumit untuk mencoba mengakomodasi suatu algoritma yang logikanya akhirnya menyiratkan semua tingkatan, saya akan mengatakan saya senang dengan logika Go yang lebih sederhana, bahkan jika itu memaksa Anda berpikir alih-alih hanya memahami konsep-konsep model aplikasi Anda.