Kendalikan dengan pola desain Proxy

Seorang teman saya - dokter medis, tidak kurang - pernah mengatakan kepada saya bahwa dia meyakinkan seorang teman untuk mengikuti ujian perguruan tinggi untuknya. Seseorang yang menggantikan orang lain dikenal sebagai proxy. Sayangnya untuk teman saya, wakilnya minum terlalu banyak pada malam sebelumnya dan gagal dalam ujian.

Dalam perangkat lunak, pola desain Proxy terbukti berguna dalam berbagai konteks. Misalnya, menggunakan Paket XML Java, Anda menggunakan proxy untuk mengakses layanan Web dengan JAX-RPC (Java API untuk panggilan prosedur jarak jauh berbasis XML). Contoh 1 menunjukkan bagaimana klien mengakses layanan Hello World Web sederhana:

Contoh 1. Proksi SOAP (Simple Object Access Protocol)

kelas publik HelloClient {public static void main (String [] args) {coba {HelloIF_Stub proxy = (HelloIF_Stub) (baru HelloWorldImpl (). getHelloIF ()); proxy ._setTargetEndpoint (args [0]); System.out.println ( proxy .sayHello ("Duke!")); } catch (Exception ex) {ex.printStackTrace (); }}}

Kode contoh 1 sangat mirip dengan contoh layanan Hello World Web yang disertakan dengan JAX-RPC. Klien mendapatkan referensi ke proxy, dan menyetel titik akhir proxy (URL layanan Web) dengan argumen baris perintah. Setelah klien memiliki referensi ke proxy, itu memanggil metode proxy sayHello(). Proksi meneruskan panggilan metode tersebut ke layanan Web, yang sering kali berada di mesin yang berbeda dari mesin klien.

Contoh 1 mengilustrasikan satu penggunaan untuk pola desain Proxy: mengakses objek jarak jauh. Proksi juga terbukti berguna untuk membuat sumber daya yang mahal sesuai permintaan, proxy virtual, dan untuk mengontrol akses ke objek, proxy perlindungan.

Jika Anda telah membaca "Hiasi Kode Java Anda" ( JavaWorld, Desember 2001), Anda mungkin melihat kesamaan antara pola desain Dekorator dan Proxy. Kedua pola menggunakan proxy yang meneruskan panggilan metode ke objek lain, yang dikenal sebagai subjek sebenarnya. Perbedaannya adalah, dengan pola Proxy, hubungan antara proxy dan subjek sebenarnya biasanya disetel pada waktu kompilasi, sedangkan dekorator dapat dibangun secara rekursif pada waktu proses. Tapi aku terlalu memaksakan diri.

Pada artikel ini, saya pertama kali memperkenalkan pola Proxy, dimulai dengan contoh proxy untuk ikon Swing. Saya menyimpulkan dengan melihat dukungan bawaan JDK untuk pola Proxy.

Catatan: Dalam dua angsuran pertama kolom ini - "Kagumilah Teman Pengembang Anda dengan Pola Desain" (Oktober 2001) dan "Hiasi Kode Java Anda" - Saya membahas pola Penghias, yang berkaitan erat dengan pola Proxy, jadi Anda mungkin ingin melihat artikel ini sebelum melanjutkan.

Pola Proxy

Proxy: Kontrol akses ke objek dengan proxy (juga dikenal sebagai pengganti atau placeholder).

Ikon ayunan, untuk alasan yang dibahas di bagian "Penerapan Proxy" di bawah, mewakili pilihan yang sangat baik untuk mengilustrasikan pola Proxy. Saya mulai dengan pengenalan singkat tentang ikon Swing, diikuti dengan diskusi tentang proxy ikon Swing.

Ikon ayunan

Ikon ayunan adalah gambar kecil yang digunakan pada tombol, menu, dan bilah alat. Anda juga dapat menggunakan ikon Ayun sendiri, seperti yang diilustrasikan pada Gambar 1.

Aplikasi yang ditunjukkan pada Gambar 1 tercantum dalam Contoh 2:

Contoh 2. Ikon ayunan

import java.awt. *; import java.awt.event. *; import javax.swing. *; // Kelas ini menguji ikon gambar. public class IconTest memperluas JFrame {private static String IMAGE_NAME = "mandrill.jpg"; private static int FRAME_X = 150, FRAME_Y = 200, FRAME_WIDTH = 268, FRAME_HEIGHT = 286; Ikon pribadi imageIcon = null, imageIconProxy = null; public void main statis (String args []) {IconTest app = new IconTest (); app.show (); } publik IconTest () {super ("Icon Test"); imageIcon = ImageIcon baru (IMAGE_NAME); setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } cat ruang kosong publik (Grafik g) {super.paint (g); Insets insets = getInsets (); imageIcon.paintIcon (this, g, insets.left, insets.top); }}

Aplikasi sebelumnya membuat ikon gambar - contoh dari javax.swing.ImageIcon- dan kemudian mengganti paint()metode untuk melukis ikon.

Mengayunkan proxy ikon gambar

Aplikasi yang ditunjukkan pada Gambar 1 adalah penggunaan yang buruk dari ikon gambar Swing karena Anda harus menggunakan ikon gambar hanya untuk gambar kecil. Batasan itu ada karena membuat gambar itu mahal, dan ImageIconcontoh membuat gambarnya saat dibuat. Jika suatu aplikasi membuat banyak gambar besar sekaligus, hal itu dapat menyebabkan penurunan kinerja yang signifikan. Selain itu, jika aplikasi tidak menggunakan semua gambarnya, akan sia-sia jika dibuat di awal.

Solusi yang lebih baik memuat gambar saat dibutuhkan. Untuk melakukannya, proxy dapat membuat ikon nyata saat pertama kali metode proxy paintIcon()dipanggil. Gambar 2 menunjukkan aplikasi yang berisi ikon gambar (di sebelah kiri) dan proxy gambar-ikon (di sebelah kanan). Gambar atas menunjukkan aplikasi tepat setelah diluncurkan. Karena ikon gambar memuat gambarnya saat dibuat, gambar ikon ditampilkan segera setelah jendela aplikasi terbuka. Sebaliknya, proxy tidak memuat gambarnya sampai dicat untuk pertama kali. Sampai gambar dimuat, proxy menggambar batas di sekelilingnya dan menampilkan "Memuat gambar ..." Gambar bawah pada Gambar 2 menunjukkan aplikasi setelah proxy memuat gambarnya.

Saya telah membuat daftar aplikasi yang ditunjukkan pada Gambar 2 di Contoh 3:

Contoh 3. Proksi ikon ayunan

import java.awt. *; import java.awt.event. *; import javax.swing. *; // Kelas ini menguji proxy virtual, yaitu proxy yang // menunda pemuatan sumber daya yang mahal (ikon) sampai // sumber daya itu dibutuhkan. public class VirtualProxyTest memperluas JFrame {private static String IMAGE_NAME = "mandrill.jpg"; private static int IMAGE_WIDTH = 256, IMAGE_HEIGHT = 256, SPACING = 5, FRAME_X = 150, FRAME_Y = 200, FRAME_WIDTH = 530, FRAME_HEIGHT = 286; Ikon pribadi imageIcon = null, imageIconProxy = null; public void main statis (String args []) {VirtualProxyTest app = new VirtualProxyTest (); app.show (); } public VirtualProxyTest () {super ("Virtual Proxy Test"); // Buat ikon gambar dan proxy ikon-gambar. imageIcon = ImageIcon baru (IMAGE_NAME); imageIconProxy = baruImageIconProxy (IMAGE_NAME, IMAGE_WIDTH, IMAGE_HEIGHT); // Tetapkan batas frame, dan default frame // operasi tutup. setBounds (FRAME_X, FRAME_Y, FRAME_WIDTH, FRAME_HEIGHT); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); } cat ruang kosong publik (Grafik g) {super.paint (g); Insets insets = getInsets (); imageIcon.paintIcon (this, g, insets.left, insets.top); imageIconProxy.paintIcon (this, g, insets.left + IMAGE_WIDTH + SPACING, // width insets.top); // tinggi}}

Contoh 3 hampir identik dengan Contoh 2, kecuali untuk penambahan proxy ikon-gambar. Aplikasi Contoh 3 membuat ikon dan proxy dalam konstruktornya, dan mengganti paint()metode untuk melukisnya. Sebelum membahas implementasi proxy, lihat Gambar 3 yang merupakan diagram kelas dari subjek nyata proxy, yaitu javax.swing.ImageIconkelas.

The javax.swing.Iconinterface, yang mendefinisikan esensi dari ikon Swing, termasuk tiga metode: paintIcon(), getIconWidth(), dan getIconHeight(). The ImageIconkelas mengimplementasikan Iconinterface, dan menambahkan metode sendiri. Ikon gambar juga menyimpan deskripsi, dan referensi ke, gambarnya.

Proxy gambar-ikon mengimplementasikan Iconantarmuka dan memelihara referensi ke ikon gambar - subjek sebenarnya - seperti yang diilustrasikan oleh diagram kelas pada Gambar 4.

The ImageIconProxykelas tercantum dalam Contoh 4.

Contoh 4. ImageIconProxy.java

// ImageIconProxy adalah proxy (atau pengganti) untuk sebuah ikon. // Proksi menunda memuat gambar sampai pertama kali // gambar diambil. Saat ikon memuat gambarnya, // proxy menggambar batas dan pesan "Memuat gambar ..." kelas ImageIconProxy mengimplementasikan javax.swing.Icon {private Icon realIcon = null; boolean isIconCreated= salah; private String imageName; lebar int pribadi, tinggi; public ImageIconProxy (String imageName, int width, int height) {this.imageName = imageName; this.width = lebar; this.height = tinggi; } public int getIconHeight () {return isIconCreated? tinggi: realIcon.getIconHeight (); } public int getIconWidth () {return isIconCreated realIcon == null? lebar: realIcon.getIconWidth (); } // Metode proxy paint () kelebihan beban untuk menggambar batas // dan pesan ("Memuat gambar ...") saat gambar // dimuat. Setelah gambar dimuat, gambar itu diambil. Perhatikan // bahwa proxy tidak memuat gambar sampai // benar-benar dibutuhkan. public void paintIcon (Komponen akhir c, Grafik g, int x, int y) { if (isIconCreated) { realIcon.paintIcon (c, g, x, y); } lain { g.drawRect(x, y, lebar-1, tinggi-1); g.drawString ("Memuat gambar ...", x + 20, y + 20); // Ikon dibuat (artinya gambar dimuat) // di utas lain. disinkronkan (ini) {SwingUtilities.invokeLater (new Runnable () {public void run () {coba {// Perlambat proses pemuatan gambar. Thread.currentThread (). sleep (2000); // ImageIcon konstruktor membuat gambar . realIcon = new ImageIcon (imageName); isIconCreated = true;} catch (InterruptedException ex) {ex.printStackTrace ();} // Cat ulang komponen ikon setelah // ikon dibuat. c.repaint (); }} ); }}}}

ImageIconProxymempertahankan referensi ke ikon nyata dengan realIconvariabel anggota. Pertama kali proxy dicat, ikon sebenarnya dibuat pada utas terpisah untuk memungkinkan persegi panjang dan string dicat (panggilan ke g.drawRect()dan g.drawString()tidak berlaku sampai paintIcon()metode kembali). Setelah ikon asli dibuat, dan oleh karena itu gambar dimuat, komponen yang menampilkan ikon tersebut dicat ulang. Gambar 5 menunjukkan diagram urutan untuk peristiwa tersebut.

Diagram urutan Gambar 5 adalah tipikal dari semua proxy: Proxy mengontrol akses ke subjek aslinya. Karena kontrol tersebut, proxy sering kali membuat instance subjek aslinya , seperti kasus proxy ikon gambar yang tercantum di Contoh 4. Instansiasi tersebut adalah salah satu perbedaan antara pola Proxy dan pola Dekorator: Dekorator jarang membuat subjek aslinya.

Dukungan bawaan JDK untuk pola desain Proxy

Pola proxy adalah salah satu pola desain terpenting karena memberikan alternatif untuk memperluas fungsionalitas dengan pewarisan. Alternatif tersebut adalah komposisi objek, di mana metode penerusan objek (proxy) memanggil ke objek tertutup (subjek nyata).

Komposisi objek lebih disukai daripada pewarisan karena, dengan komposisi, objek penutup hanya dapat memanipulasi objek tertutup mereka melalui antarmuka objek tertutup, yang menghasilkan kopling longgar antar objek. Sebaliknya, dengan pewarisan, kelas digabungkan erat ke kelas dasarnya karena internal kelas dasar terlihat oleh ekstensinya. Karena visibilitas tersebut, warisan sering disebut sebagai penggunaan kembali kotak putih. Di sisi lain, dengan komposisi, bagian dalam objek penutup tidak terlihat oleh objek tertutup (dan sebaliknya); oleh karena itu, komposisi tersebut sering disebut sebagai penggunaan kembali kotak hitam. Semua hal dianggap sama, penggunaan kembali kotak hitam (komposisi) lebih disukai daripada penggunaan kembali kotak putih (warisan) karena kopling yang longgar menghasilkan sistem yang lebih mudah dibentuk dan fleksibel.

Karena pola Proxy sangat penting, J2SE 1.3 (Platform Java 2, Edisi Standar) dan seterusnya mendukungnya secara langsung. Dukungan yang melibatkan tiga kelas dari java.lang.reflectpaket: Proxy, Method, dan InvocationHandler. Contoh 5 menunjukkan contoh sederhana yang menggunakan dukungan JDK untuk pola Proxy: