Buku Dr. Yan Hong "JAVA and Patterns" dimulai dengan deskripsi pola Pengunjung:
Pola pengunjung adalah pola perilaku objek. Tujuan dari pola pengunjung adalah untuk merangkum beberapa operasi yang diterapkan pada elemen struktur data tertentu. Setelah operasi ini perlu diubah, struktur data yang menerima operasi ini tetap tidak berubah.
Konsep pengiriman
Tipe ketika suatu variabel dideklarasikan disebut tipe statis dari variabel tersebut (Tipe Statis), dan sebagian orang menyebut tipe statis sebagai tipe semu (Tipe Tampak); dan tipe sebenarnya dari objek yang direferensikan oleh variabel juga disebut tipe sebenarnya dari variabel (Actual Type). Misalnya:
Copy kode kodenya sebagai berikut:
Daftar daftar = null;
daftar = Daftar Array baru();
Daftar variabel dideklarasikan, tipe statisnya (juga disebut tipe jelas) adalah Daftar, dan tipe sebenarnya adalah ArrayList.
Pemilihan metode berdasarkan jenis objek pengirimannya terbagi menjadi dua jenis yaitu pengiriman statis dan pengiriman dinamis.
Pengiriman Statis terjadi pada waktu kompilasi, dan pengiriman terjadi berdasarkan informasi tipe statis. Pengiriman statis sudah tidak asing lagi bagi kita. Metode yang berlebihan adalah pengiriman statis.
Pengiriman Dinamis terjadi selama runtime, dan pengiriman dinamis secara dinamis menggantikan metode.
pengiriman statis
Java mendukung pengiriman statis melalui metode yang berlebihan. Misalnya cerita Mozi menunggang kuda, Mozi bisa menunggangi kuda putih atau kuda hitam. Diagram kelas Mozi dan kuda putih, kuda hitam dan kuda adalah sebagai berikut:
Dalam sistem ini, Mozi diwakili oleh kelas Mozi. Kodenya adalah sebagai berikut:
kelas publik Mozi {
perjalanan umum (Kuda h){
System.out.println("menunggang kuda");
}
perjalanan umum (Kuda Putih ap){
System.out.println("menunggang kuda putih");
}
perjalanan umum (BlackHorse bh){
System.out.println("Naik kuda hitam");
}
public static void main(String[] args) {
Kuda wh = Kuda Putih baru();
Kuda bh = Kuda Hitam baru();
Mozi mozi = Mozi baru();
mozi.ride(wh);
mozi.ride(bh);
}
}
Jelas sekali, metode ride() kelas Mozi kelebihan beban dari tiga metode. Ketiga metode ini masing-masing menerima parameter Horse, WhiteHorse, BlackHorse, dan tipe lainnya.
Lalu apa hasil yang akan dicetak program saat dijalankan? Hasilnya adalah program mencetak dua baris "punggung kuda" yang sama. Dengan kata lain, Mozi mengetahui bahwa yang ditungganginya hanyalah kuda.
Mengapa? Kedua pemanggilan metode ride() meneruskan parameter yang berbeda, yaitu wh dan bh. Walaupun mempunyai tipe nyata yang berbeda, namun tipe statisnya semuanya sama, yaitu tipe Kuda.
Pengiriman metode kelebihan beban didasarkan pada tipe statis, dan proses pengiriman ini selesai pada waktu kompilasi.
pengiriman dinamis
Java mendukung pengiriman dinamis melalui penggantian metode. Contoh cerita kuda makan rumput, kodenya sebagai berikut:
Copy kode kodenya sebagai berikut:
Kuda kelas publik {
kekosongan publik makan(){
System.out.println("Kuda pemakan rumput");
}
}
Copy kode kodenya sebagai berikut:
kelas publik BlackHorse memperluas Horse {
@Mengesampingkan
kekosongan publik makan() {
System.out.println("Kuda hitam memakan rumput");
}
}
Copy kode kodenya sebagai berikut:
Klien kelas publik {
public static void main(String[] args) {
Kuda h = Kuda Hitam baru();
panas();
}
}
Tipe statis dari variabel h adalah Horse, dan tipe sebenarnya adalah BlackHorse. Jika method eat() pada baris terakhir di atas memanggil method eat() dari kelas BlackHorse, maka yang tercetak di atas adalah "Rumput Pemakan Kuda Hitam"; ) metode kelas Kuda, maka yang tercetak adalah “kuda makan rumput”.
Oleh karena itu, inti masalahnya adalah compiler Java tidak selalu mengetahui kode mana yang akan dieksekusi selama kompilasi, karena compiler hanya mengetahui tipe statis objek, tetapi tidak mengetahui tipe objek dan metode sebenarnya; panggilan didasarkan pada tipe Real objek, bukan tipe statis. Dengan cara ini, metode eat() pada baris terakhir di atas memanggil metode eat() dari kelas BlackHorse, dan mencetak "kuda hitam makan rumput".
jenis pengiriman
Objek yang dimiliki suatu metode disebut penerima metode. Penerima metode dan parameter metode secara kolektif disebut volume metode. Misalnya salinan kode kelas Test pada contoh di bawah ini adalah sebagai berikut:
Tes kelas publik {
pencetakan kekosongan publik(String str){
Sistem.keluar.println(str);
}
}
Pada kelas di atas, metode print() dimiliki oleh objek Test, sehingga penerimanya juga merupakan objek Test. Metode print() memiliki parameter bernama str, dan tipenya adalah String.
Bergantung pada berapa banyak jenis pengiriman kuantitas yang dapat didasarkan, bahasa berorientasi objek dapat dibagi menjadi bahasa pengiriman tunggal (Uni-Dispatch) dan bahasa multi-pengiriman (Multi-Dispatch). Bahasa pengiriman tunggal memilih metode berdasarkan jenis satu instance, sedangkan bahasa multi-pengiriman memilih metode berdasarkan jenis lebih dari satu instance.
Baik C++ dan Java adalah bahasa pengiriman tunggal, dan contoh bahasa multi-pengiriman termasuk CLOS dan Cecil. Menurut perbedaan ini, Java adalah bahasa pengiriman tunggal yang dinamis, karena pengiriman dinamis bahasa ini hanya memperhitungkan jenis penerima metode, dan merupakan bahasa multi-pengiriman statis, karena bahasa ini mengirimkan metode yang kelebihan beban jenis penerima metode dan jenis semua parameter metode diperhitungkan.
Dalam bahasa yang mendukung pengiriman tunggal dinamis, ada dua kondisi yang menentukan operasi mana yang akan dipanggil oleh permintaan: kondisi pertama adalah nama permintaan, dan tipe penerima sebenarnya. Pengiriman tunggal membatasi proses pemilihan metode sehingga hanya satu contoh yang dapat dipertimbangkan, yang biasanya merupakan penerima metode. Dalam bahasa Java, jika suatu operasi dilakukan pada objek yang tipenya tidak diketahui, maka pengujian tipe sebenarnya dari objek tersebut hanya akan terjadi satu kali. Ini adalah karakteristik pengiriman tunggal dinamis.
pengiriman ganda
Suatu metode memutuskan untuk mengeksekusi kode yang berbeda berdasarkan tipe dua variabel. Ini adalah "pengiriman ganda". Bahasa Java tidak mendukung pengiriman ganda dinamis, yang berarti Java tidak mendukung pengiriman ganda dinamis. Namun dengan menggunakan pola desain, pengiriman ganda dinamis juga dapat diimplementasikan dalam bahasa Java.
Di Java, dua pengiriman dapat dicapai melalui dua pemanggilan metode. Diagram kelasnya adalah sebagai berikut:
Ada dua benda pada gambar, yang di sebelah kiri disebut Barat dan yang di sebelah kanan disebut Timur. Sekarang objek Barat terlebih dahulu memanggil metode goEast() objek Timur, dan meneruskan dirinya sendiri. Saat objek East dipanggil, ia langsung mengetahui siapa pemanggilnya berdasarkan parameter yang dimasukkan, sehingga metode goWest() dari objek "pemanggil" dipanggil secara bergantian. Melalui dua panggilan, kendali program diserahkan kepada dua objek secara bergantian. Diagram urutannya adalah sebagai berikut:
Dengan cara ini, ada dua pemanggilan metode. Kontrol program dilewatkan di antara dua objek. Pertama, kontrol tersebut diteruskan dari objek Barat ke objek Timur, dan kemudian diteruskan kembali ke objek Barat.
Namun mengembalikan bola saja tidak menyelesaikan masalah distribusi ganda. Kuncinya adalah bagaimana menggunakan dua panggilan ini dan fungsi pengiriman tunggal dinamis dari bahasa Java untuk memicu dua pengiriman tunggal selama proses penerusan ini.
Pengiriman tunggal dinamis dalam bahasa Java terjadi ketika subkelas menimpa metode kelas induk. Dengan kata lain, baik Barat maupun Timur harus ditempatkan dalam hierarki tipenya masing-masing, seperti yang ditunjukkan di bawah ini:
kode sumber
Kode salinan kelas Barat adalah sebagai berikut:
kelas abstrak publik Barat {
abstrak publik void goWest1(SubEast1 timur);
abstrak publik void goWest2(SubEast2 timur);
}
Kode kode salinan kelas SubWest1 adalah sebagai berikut:
kelas publik SubWest1 meluas ke Barat{
@Mengesampingkan
public void goWest1(SubEast1 timur) {
System.out.println("SubBarat1 + " + timur.Namaku1());
}
@Mengesampingkan
public void goWest2(SubEast2 timur) {
System.out.println("SubBarat1 + " + timur.Namasaya2());
}
}
Kelas SubBarat 2
Copy kode kodenya sebagai berikut:
kelas publik SubWest2 meluas ke Barat{
@Mengesampingkan
public void goWest1(SubEast1 timur) {
System.out.println("SubBarat2 + " + timur.Namasaya1());
}
@Mengesampingkan
public void goWest2(SubEast2 timur) {
System.out.println("SubBarat2 + " + timur.Namasaya2());
}
}
Kode salinan kelas Timur adalah sebagai berikut:
kelas abstrak publik Timur {
abstrak publik batal pergiTimur(Barat barat);
}
Kode kode salinan kelas SubEast1 adalah sebagai berikut:
kelas publik SubEast1 meluas ke Timur{
@Mengesampingkan
public void goEast(Barat barat) {
barat.goWest1(ini);
}
String publik Namaku1(){
kembalikan "SubTimur1";
}
}
Kode kode salinan kelas SubEast2 adalah sebagai berikut:
kelas publik SubEast2 meluas ke Timur{
@Mengesampingkan
public void goEast(Barat barat) {
barat.goWest2(ini);
}
String publik Namaku2(){
kembalikan "SubTimur2";
}
}
Kode salinan kelas klien adalah sebagai berikut:
Klien kelas publik {
public static void main(String[] args) {
//kombinasi 1
Timur timur = SubEast1();
Barat barat = new SubWest1();
timur.pergiTimur(barat);
//kombinasi 2
timur = baru SubEast1();
barat = baru SubWest2();
timur.pergiTimur(barat);
}
}
Hasil yang berjalan adalah sebagai berikut. Salin kodenya sebagai berikut:
SubBarat1 + SubTimur1
SubBarat2 + SubTimur1
Saat sistem sedang berjalan, objek SubWest1 dan SubEast1 dibuat terlebih dahulu, lalu klien memanggil metode goEast() dari SubEast1 dan meneruskan objek SubWest1. Karena objek SubEast1 menggantikan metode goEast() superclass East-nya, pengiriman tunggal dinamis terjadi pada saat ini. Saat objek SubEast1 menerima panggilan, ia akan mendapatkan objek SubWest1 dari parameternya, sehingga ia segera memanggil metode goWest1() dari objek ini dan meneruskannya sendiri. Karena objek SubEast1 memiliki hak untuk memilih objek mana yang akan dipanggil, pengiriman metode dinamis lainnya dilakukan saat ini.
Saat ini objek SubWest1 telah memperoleh objek SubEast1. Dengan memanggil metode myName1() objek ini, Anda dapat mencetak nama Anda sendiri dan nama objek SubEast. Diagram urutannya adalah sebagai berikut:
Karena salah satu dari dua nama ini berasal dari hierarki Timur dan yang lainnya berasal dari hierarki Barat, maka kombinasi keduanya ditentukan secara dinamis. Ini adalah mekanisme implementasi pengiriman ganda dinamis.
Struktur pola pengunjung
Pola pengunjung cocok untuk sistem dengan struktur data yang relatif belum ditentukan. Pola ini memisahkan hubungan antara struktur data dan operasi yang bekerja pada struktur, sehingga rangkaian operasi dapat berkembang secara relatif bebas. Diagram pola pengunjung yang disederhanakan ditunjukkan di bawah ini:
Setiap node dari struktur data dapat menerima panggilan dari pengunjung. Node ini meneruskan objek node ke objek pengunjung, dan objek pengunjung pada gilirannya melakukan operasi objek node. Proses ini disebut "pengiriman ganda". Node memanggil pengunjung, meneruskan dirinya sendiri, dan pengunjung mengeksekusi algoritma terhadap node ini. Diagram kelas skema untuk pola Pengunjung ditunjukkan di bawah ini:
Peran yang terlibat dalam mode pengunjung adalah sebagai berikut:
● Peran pengunjung abstrak (Pengunjung) : mendeklarasikan satu atau lebih operasi metode untuk membentuk antarmuka yang harus diterapkan oleh semua peran pengunjung tertentu.
● Peran Pengunjung Beton (ConcreteVisitor) : mengimplementasikan antarmuka yang dideklarasikan oleh pengunjung abstrak, yaitu setiap operasi akses yang dideklarasikan oleh pengunjung abstrak.
● Peran node abstrak (Node) : mendeklarasikan operasi penerimaan dan menerima objek pengunjung sebagai parameter.
● Peran ConcreteNode : mengimplementasikan operasi penerimaan yang ditentukan oleh node abstrak.
● Peran objek struktur (ObjectStructure) : memiliki tanggung jawab sebagai berikut, dapat melintasi semua elemen dalam struktur; jika perlu, menyediakan antarmuka tingkat tinggi sehingga objek pengunjung dapat mengakses setiap elemen, jika perlu, dapat dirancang sebagai objek komposit atau Koleksi, seperti Daftar atau Kumpulan.
kode sumber
Seperti yang Anda lihat, peran pengunjung abstrak menyiapkan operasi akses untuk setiap node tertentu. Karena ada dua node, ada dua operasi akses yang sesuai.
Copy kode kodenya sebagai berikut:
antarmuka publik Pengunjung {
/**
* Sesuai dengan operasi akses NodeA
*/
kunjungan batal publik (node NodeA);
/**
* Sesuai dengan operasi akses NodeB
*/
kunjungan batal publik (nodeB node);
}
Kode salinan kelas pengunjungA tertentu adalah sebagai berikut:
kelas publik VisitorA mengimplementasikan Visitor {
/**
* Sesuai dengan operasi akses NodeA
*/
@Mengesampingkan
kunjungan kekosongan publik (simpul NodeA) {
Sistem.keluar.println(node.operationA());
}
/**
* Sesuai dengan operasi akses NodeB
*/
@Mengesampingkan
kunjungan kekosongan publik (simpul NodeB) {
Sistem.keluar.println(node.operationB());
}
}
Kode salinan kelas VisitorB pengunjung tertentu adalah sebagai berikut:
kelas publik VisitorB mengimplementasikan Visitor {
/**
* Sesuai dengan operasi akses NodeA
*/
@Mengesampingkan
kunjungan kekosongan publik (simpul NodeA) {
Sistem.keluar.println(node.operationA());
}
/**
* Sesuai dengan operasi akses NodeB
*/
@Mengesampingkan
kunjungan kekosongan publik (simpul NodeB) {
Sistem.keluar.println(node.operationB());
}
}
Kode kode salinan kelas node abstrak adalah sebagai berikut:
kelas abstrak publik Node {
/**
* Terima operasi
*/
abstrak publik batal diterima(Pengunjung pengunjung);
}
Kelas simpul tertentu NodeA
Copy kode kodenya sebagai berikut:
kelas publik NodeA memperluas Node{
/**
* Terima operasi
*/
@Mengesampingkan
public void terima(Pengunjung pengunjung) {
pengunjung.kunjungi(ini);
}
/**
*Metode khusus NodeA
*/
operasi String publikA(){
kembalikan "NodeA";
}
}
Kelas simpul tertentu NodeB
Copy kode kodenya sebagai berikut:
NodeB kelas publik memperluas Node{
/**
*Terima metode
*/
@Mengesampingkan
public void terima(Pengunjung pengunjung) {
pengunjung.kunjungi(ini);
}
/**
*Metode khusus NodeB
*/
operasi String publikB(){
kembalikan "NodeB";
}
}
Kelas peran objek struktural. Peran objek struktural ini menampung koleksi dan menyediakan metode add() ke dunia luar sebagai operasi manajemen untuk koleksi tersebut. Dengan memanggil metode ini, node baru dapat ditambahkan secara dinamis.
Copy kode kodenya sebagai berikut:
Struktur Objek kelas publik {
Daftar pribadi<Node> node = ArrayList<Node>();
/**
* Jalankan operasi metode
*/
tindakan pembatalan publik(Pengunjung pengunjung){
untuk(simpul simpul : simpul)
{
node.accept(pengunjung);
}
}
/**
* Tambahkan elemen baru
*/
public void add(Node simpul){
node.tambahkan(simpul);
}
}
Kode salinan kelas klien adalah sebagai berikut:
Klien kelas publik {
public static void main(String[] args) {
//Membuat objek struktur
ObjectStructure os = ObjectStructure baru();
//Tambahkan node ke struktur
os.add(NodeA baru());
//Tambahkan node ke struktur
os.add(NodeB baru());
//Buat pengunjung
Pengunjung pengunjung = Pengunjung baruA();
os.action(pengunjung);
}
}
Meskipun struktur pohon objek yang kompleks dengan banyak simpul cabang tidak muncul dalam implementasi skema ini, dalam sistem sebenarnya pola pengunjung biasanya digunakan untuk menangani struktur pohon objek yang kompleks, dan pola pengunjung dapat digunakan untuk menangani masalah struktur pohon yang mencakup banyak hierarki . Di sinilah pola pengunjung begitu kuat.
Diagram urutan proses persiapan
Pertama, klien ilustratif ini membuat objek struktur dan kemudian meneruskan objek NodeA baru dan objek NodeB baru.
Kedua, klien membuat objek VisitorA dan meneruskan objek ini ke objek struktur.
Kemudian, klien memanggil metode manajemen agregasi objek struktur untuk menambahkan node NodeA dan NodeB ke objek struktur.
Terakhir, klien memanggil metode tindakan action() dari objek struktur untuk memulai proses akses.
Akses diagram urutan proses
Objek struktur akan melintasi semua node dalam koleksi yang disimpannya, yang dalam sistem ini adalah node NodeA dan NodeB. Pertama, NodeA akan diakses. Akses ini terdiri dari operasi berikut:
(1) Metode terima () dari objek NodeA dipanggil dan objek VisitorA itu sendiri diteruskan;
(2) Objek NodeA pada gilirannya memanggil metode akses objek VisitorA dan meneruskan objek NodeA itu sendiri;
(3) Objek VisitorA memanggil metode unik OperationA() dari objek NodeA.
Dengan demikian, proses pengiriman ganda selesai. Kemudian NodeB akan diakses. Proses aksesnya sama dengan proses akses NodeA, yang tidak akan dijelaskan di sini.
Keuntungan Pola Pengunjung
● Ekstensibilitas yang baik dapat menambahkan fungsi baru pada elemen struktur objek tanpa mengubah elemen struktur objek.
● Penggunaan kembali yang baik memungkinkan pengunjung untuk menentukan fungsi umum pada seluruh struktur objek, sehingga meningkatkan tingkat penggunaan kembali.
● Memisahkan perilaku yang tidak relevan Anda dapat menggunakan pengunjung untuk memisahkan perilaku yang tidak relevan, dan merangkum perilaku terkait untuk membentuk pengunjung, sehingga fungsi setiap pengunjung relatif tunggal.
Kekurangan Pola Pengunjung
● Sulit untuk mengubah struktur objek. Ini tidak cocok untuk situasi di mana kelas dalam struktur objek sering berubah. Karena struktur objek berubah, antarmuka pengunjung dan implementasi pengunjung harus berubah, yang terlalu mahal.
● Melanggar pola Enkapsulasi Pengunjung biasanya memerlukan struktur objek untuk membuka data internal kepada pengunjung dan ObjectStructrue, yang memutus enkapsulasi objek.