Apabila kita bercakap tentang pengaturcaraan, biasanya kita bermaksud menulis sekumpulan fungsi yang mengubah suai dan berinteraksi dengan beberapa data. Pengaturcaraan Berorientasikan Objek (OOP) ialah model pengaturcaraan yang sebaliknya memfokuskan pada "objek" yang mengandungi data dan dan mempunyai beberapa fungsi berkaitan yang dilampirkan padanya. Pengaturcaraan Berorientasikan Objek mempunyai empat tiang: Pewarisan, Enkapsulasi, Polimorfisme, dan Abstraksi. Dalam blog ini, kami akan melihat bagaimana anda boleh melaksanakan setiap daripada mereka di Golang dengan contoh. Beberapa idea asas tentang OOP disyorkan, tetapi jika tidak, saya akan memberikan pengenalan ringkas tentang maksud keempat-empat tiang itu.
Idea teras Pengaturcaraan Berorientasikan Objek boleh disimpulkan dalam titik tumpu ini:
Mari kita lihat beberapa kod di Golang untuk memahami tiga konsep ini:
package main import "fmt" type Batman struct { actor string year int } func (b Batman) SayImBatman() { fmt.Printf("I'm %s and I'm Batman from year %d\n", b.actor, b.year) } func main() { b1 := Batman{actor: "Michael Keaton", year: 1989} b2 := Batman{actor: "Christian Bale", year: 2005} b1.SayImBatman() b2.SayImBatman() }
Di Golang, kelas hanyalah jenis yang ditakrifkan oleh kami. Jenis ini tidak semestinya struct tetapi biasanya ia adalah kerana dalam OOP kita bekerja dengan koleksi data, yang boleh daripada sebarang jenis (rentetan, int, dll.).
Kelas ialah cetak biru untuk objek. Setiap kali anda membuat instantiate kelas, objek terbentuk. Dalam contoh ini, b1 dan b2 ialah objek kelas Batman.
Fungsi SayImBatman boleh dipanggil pada mana-mana objek kelas. Memandangkan ia terikat dengan kelas Batman, bukannya memanggilnya fungsi biasa, ia dipanggil kaedah kelas.
Saya rasa ini sepatutnya menjelaskan asas OOP yang cukup untuk anda meneruskan ke bahagian seterusnya, di mana kita melihat empat tiang OOP.
Warisan memperkenalkan konsep kelas ibu bapa dan anak dalam OOP. Kelas anak ialah kelas yang diperoleh daripada kelas induk dan mewarisi semua kaedah dan sifatnya (data). Mari kita lihat beberapa kod yang akan membantu kita memahami perkara ini:
package main import "fmt" type Hero struct { team string } type Batman struct { Hero name string } type Ironman struct { Hero power int } func (h Hero) SayTeam() { fmt.Println("My Team is", h.team) } func (b Batman) SayImBatman() { fmt.Printf("I'm %s and I'm Batman\n", b.name) } func (i Ironman) SayPowerLevel() { fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power) } func main() { b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"} i1 := Ironman{Hero{team: "Avengers"}, 23} b1.SayImBatman() b1.SayTeam() i1.SayPowerLevel() i1.SayTeam() }
Dalam contoh ini, Batman dan Ironman ialah kelas kanak-kanak kelas induk Hero. Mereka mempunyai akses kepada sifat kelas induk mereka, iaitu, pasukan, dan kaedahnya, iaitu, SayTeam. Seperti yang anda boleh lihat semasa mengisytiharkan contoh b1 dan i1, kami menentukan sifat kelas induk serta sifat khusus mereka untuk kelas masing-masing. Kedua-duanya boleh memanggil kaedah SayTeam yang ditakrifkan pada kelas induk. Tetapi mereka juga mempunyai sifat dan kaedah berasingan yang unik untuk setiap satu.
Golang melaksanakan pewarisan menggunakan gubahan (menggunakan struct di dalam struct). Ia tidak mempunyai warisan berasaskan kelas terbina seperti bahasa OOP lain seperti C atau Java.
Encapsulation ialah prinsip menyembunyikan sifat dalaman objek dan tidak membenarkannya diubah suai secara langsung. Sebaliknya, ia bergantung pada penyediaan kaedah untuk mendapatkan dan mengemas kini sifat ini. Mari lihat contoh untuk memahami perkara ini dengan lebih baik:
package main import "fmt" type Batman struct { actor string year int } func (b Batman) SayImBatman() { fmt.Printf("I'm %s and I'm Batman from year %d\n", b.actor, b.year) } func main() { b1 := Batman{actor: "Michael Keaton", year: 1989} b2 := Batman{actor: "Christian Bale", year: 2005} b1.SayImBatman() b2.SayImBatman() }
package main import "fmt" type Hero struct { team string } type Batman struct { Hero name string } type Ironman struct { Hero power int } func (h Hero) SayTeam() { fmt.Println("My Team is", h.team) } func (b Batman) SayImBatman() { fmt.Printf("I'm %s and I'm Batman\n", b.name) } func (i Ironman) SayPowerLevel() { fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power) } func main() { b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"} i1 := Ironman{Hero{team: "Avengers"}, 23} b1.SayImBatman() b1.SayTeam() i1.SayPowerLevel() i1.SayTeam() }
Di Golang, sifat dan kaedah yang dieksport keluar daripada pakej bermula dengan huruf besar. Apabila kami mentakrifkan aktor dan tahun dengan huruf kecil dalam pakej utils, kami memastikan ia tidak boleh diubah suai secara langsung. Sebaliknya, seperti yang anda lihat dalam fail main.go, anda perlu menggunakan kaedah yang dieksport (yang bermula dengan huruf besar) - GetActor, SetActor, dsb., untuk mengambil dan mengubah suainya.
Inilah maksud enkapsulasi - memastikan anda menghalang perubahan yang tidak disengajakan pada data dan sebaliknya menyediakan kaedah untuk berinteraksi dengan data dengan selamat.
Satu perkara yang anda akan nampak berbeza ialah dalam semua kaedah untuk kelas Batman, kami menggunakan penerima penunjuk *Batman dan bukannya penerima nilai Batman seperti yang kami lakukan dalam contoh terdahulu. Ini kerana kita mahu dapat mengubah suai struct asal dalam kaedah Set. Dan di Golang, amalan terbaik ialah jika sesetengah kaedah memerlukan penerima penunjuk, anda menjadikan semua kaedah menggunakan penerima penunjuk untuk konsistensi. Itulah sebabnya kaedah Dapatkan menggunakan penerima penuding juga walaupun ia tidak mengubah suai struktur asal.
Selain itu, satu lagi perkara yang perlu diambil perhatian ialah hanya kerana kami menggunakan penerima penuding, kami tidak perlu melakukan ini: (&b1).GetActor. Dalam Golang, fungsi dengan hujah penunjuk mesti mengambil penunjuk, tetapi kaedah dengan penerima penunjuk boleh sama ada mengambil nilai atau penunjuk sebagai penerima.
TL;DR: Golang secara automatik menterjemah b1.GetActor sebagai (&b1).GetActor memandangkan kaedah GetActor mempunyai penerima penuding, tetapi ia tidak akan menterjemahkan GetActor(b1) kepada GetActor(&b1) sekiranya GetActor menggunakan fungsi biasa hujah penunjuk.
Dua tiang OOP seterusnya boleh digabungkan kerana sampel kod untuknya akan kelihatan hampir serupa. Polimorfisme merujuk kepada amalan pengaturcaraan di mana dua objek berbeza dari dua kelas berbeza boleh dianggap sebagai objek superclass biasa yang sama. Ini bermakna anda boleh memanggil fungsi yang sama pada dua objek berbeza seolah-olah ia adalah objek kelas yang sama. Ini sepatutnya mula memberi anda bauan antara muka yang terlibat :)
Mari lihat beberapa kod untuk memahami perkara ini dengan lebih baik:
package main import "fmt" type Batman struct { actor string year int } func (b Batman) SayImBatman() { fmt.Printf("I'm %s and I'm Batman from year %d\n", b.actor, b.year) } func main() { b1 := Batman{actor: "Michael Keaton", year: 1989} b2 := Batman{actor: "Christian Bale", year: 2005} b1.SayImBatman() b2.SayImBatman() }
Dalam contoh ini, fungsi StartFight boleh menghantar kedua-dua objek b1 dan i1 walaupun ia tidak berkaitan antara satu sama lain. Cuba fahami cara ini berbeza daripada Warisan, di mana kelas anak mempunyai akses kepada kaedah kelas induk. Dalam contoh ini, tiada kelas anak dan ibu bapa (dan tiada kaedah yang dikongsi juga). Sebaliknya, dua objek berbeza sedang dilayan sebagai sama oleh fungsi: ini dipanggil Polimorfisme.
Kini, ini juga boleh dianggap sebagai contoh Abstraksi. Abstraksi, seperti namanya, ialah amalan pengaturcaraan untuk menyembunyikan butiran pelaksanaan dan sebaliknya hanya menyediakan fungsi yang mengurus perkara untuk anda. Dalam contoh ini, anda tidak perlu mengambil berat bagaimana kaedah wira individu dikonfigurasikan. Anda boleh terus menggunakan fungsi StartFight pada bila-bila masa anda mahu menggunakan mana-mana fungsi Fight hero. Dengan cara ini, butiran pelaksanaan kekal tersembunyi daripada pengguna dan hanya butiran penting yang terdedah.
Sekarang kembali kepada Polimorfisme, terdapat dua lagi contoh biasa, dan itu ialah kaedah mengatasi dan terlebih beban.
Penggantian kaedah merujuk kepada kelas anak yang mentakrifkan pelaksanaan kaedah mereka sendiri yang ditakrifkan pada kelas induk. Pelaksanaan ini kini digunakan dan bukannya pelaksanaan kelas induk asal. Mari ambil kod yang kami gunakan untuk warisan sebelum ini dan lihat bagaimana ia kelihatan dengan kaedah mengatasi:
package main import "fmt" type Hero struct { team string } type Batman struct { Hero name string } type Ironman struct { Hero power int } func (h Hero) SayTeam() { fmt.Println("My Team is", h.team) } func (b Batman) SayImBatman() { fmt.Printf("I'm %s and I'm Batman\n", b.name) } func (i Ironman) SayPowerLevel() { fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power) } func main() { b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"} i1 := Ironman{Hero{team: "Avengers"}, 23} b1.SayImBatman() b1.SayTeam() i1.SayPowerLevel() i1.SayTeam() }
Output program ini ialah:
//oops-in-go/utils/utils.go package utils type Batman struct { actor string year int } func (b *Batman) GetActor() string { return b.actor } func (b *Batman) GetYear() int { return b.year } func (b *Batman) SetActor(actor string) { b.actor = actor } func (b *Batman) SetYear(year int) { b.year = year }
Objek kelas Batman kini menggunakan kaedah SayTeam mereka sendiri dan bukannya kaedah kelas Hero induk. Memandangkan kelas Ironman tidak mempunyai kaedah SayTeam sendiri, objeknya masih menggunakan kaedah kelas induknya. Inilah maksud Penggantian Kaedah, kelas anak "mengatasi" kaedah yang ditakrifkan pada kelas induk.
Ini merujuk kepada fungsi yang sama yang boleh mengambil berbilang hujah yang berbeza. Argumen ini mungkin berbeza dalam bilangan atau jenis. Golang menyediakan dua cara untuk mencapainya: melalui fungsi variadik dan satu lagi melalui antara muka.
Mari lihat kod untuk kedua-duanya, yang akan membantu anda memahami dengan lebih baik:
// oops-in-go/main.go package main import ( "fmt" "oops-in-go/utils" ) func main() { b1 := utils.Batman{} b1.SetActor("Michael Keaton") b1.SetYear(1989) fmt.Printf("I'm %s and I'm Batman from year %d\n", b1.GetActor(), b1.GetYear()) b1.SetActor("Christian Bale") b1.SetYear(2005) fmt.Printf("I'm %s and I'm Batman from year %d\n", b1.GetActor(), b1.GetYear()) }
Di sini anda boleh "membebankan" fungsi listMembers dengan sebarang bilangan hujah.
package main import "fmt" type Hero interface { Fight() } type Batman struct { weapon string } type Ironman struct { weapon string } func (b Batman) Fight() { fmt.Printf("Batman hits with a %s\n", b.weapon) } func (i Ironman) Fight() { fmt.Printf("Ironman hits with a %s\n", i.weapon) } func StartFight(h Hero) { fmt.Println("Fight has started.") h.Fight() } func main() { b1 := Batman{"Batarang"} i1 := Ironman{"Repulsor rays"} StartFight(b1) StartFight(i1) }
Output program ini ialah:
package main import "fmt" type Hero struct { team string } type Batman struct { Hero name string } type Ironman struct { Hero power int } func (h Hero) SayTeam() { fmt.Println("My Team is", h.team) } func (b Batman) SayImBatman() { fmt.Printf("I'm %s and I'm Batman\n", b.name) } func (i Ironman) SayPowerLevel() { fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power) } func (b Batman) SayTeam() { fmt.Printf("I'm Batman and my team is %s\n", b.team) } func main() { b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"} i1 := Ironman{Hero{team: "Avengers"}, 23} b1.SayImBatman() b1.SayTeam() i1.SayPowerLevel() i1.SayTeam() }
Di sini kami "membebankan" kaedah saySomething untuk mengambil hujah pelbagai jenis. Kami mengambil antara muka kosong sebagai hujah, yang boleh menjadi apa-apa jenis, kemudian semak jenisnya menggunakan bekas suis dan cetak output dengan sewajarnya.
Saya sangat sedar bahawa ini adalah bacaan yang panjang, dan jika anda kekal sehingga akhir, saya ingin anda tahu bahawa saya sangat gembira :) Saya sangat berharap anda belajar banyak perkara baru tentang Pengaturcaraan Berorientasikan Objek dan cara melaksanakannya di Golang. Saya menulis blog tentang konsep teknikal yang berbeza di tapak web saya dan jika anda berminat untuk mempelajari perkara baharu, saya syorkan anda mendaftar untuk surat berita saya.
Atas ialah kandungan terperinci Pengenalan kepada Pengaturcaraan Berorientasikan Objek (OOP) di Golang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!