Tampak dalam Observer

Belum lama ini kopling saya habis, jadi saya menyeret Jeep saya ke dealer lokal. Saya tidak mengenal siapa pun di dealer itu, dan tidak ada dari mereka yang mengenal saya, jadi saya memberi mereka nomor telepon saya sehingga mereka dapat memberi tahu saya dengan perkiraan. Pengaturan itu bekerja dengan sangat baik sehingga kami melakukan hal yang sama ketika pekerjaan itu selesai. Karena ini semua ternyata sempurna bagi saya, saya curiga departemen servis di dealer menerapkan pola yang sama dengan sebagian besar pelanggannya.

Pola mempublikasikan-berlangganan ini, di mana pengamat mendaftar dengan subjek dan kemudian menerima pemberitahuan , cukup umum, baik dalam kehidupan sehari-hari maupun di dunia virtual pengembangan perangkat lunak. Faktanya, pola Observer , seperti yang diketahui, adalah salah satu kunci utama pengembangan perangkat lunak berorientasi objek karena pola ini memungkinkan objek yang berbeda untuk berkomunikasi. Kemampuan itu memungkinkan Anda memasukkan objek ke dalam kerangka kerja pada waktu proses, yang memungkinkan perangkat lunak yang sangat fleksibel, dapat diperluas, dan dapat digunakan kembali.

Catatan: Anda dapat mengunduh kode sumber artikel ini dari Sumber.

Pola Observer

Pada Design Patterns , penulis mendeskripsikan pola Observer seperti ini:

Tentukan ketergantungan satu ke banyak antar objek sehingga ketika satu objek berubah status, semua dependensinya diberitahukan dan diperbarui secara otomatis.

Pola Pengamat memiliki satu subjek dan berpotensi banyak pengamat. Pengamat mendaftar dengan subjek, yang memberi tahu pengamat saat peristiwa terjadi. Contoh prototypical Observer adalah antarmuka pengguna grafis (GUI) yang secara bersamaan menampilkan dua tampilan dari satu model; pandangan mendaftar dengan model, dan ketika model berubah, itu memberitahu pandangan, yang memperbarui sesuai. Mari kita lihat cara kerjanya.

Pengamat beraksi

Aplikasi yang ditunjukkan pada Gambar 1 berisi satu model dan dua tampilan. Nilai model, yang merepresentasikan perbesaran gambar, dimanipulasi dengan menggerakkan kenop slider. Tampilan, yang dikenal sebagai komponen di Swing, adalah label yang menunjukkan nilai model dan panel gulir yang menskalakan gambar sesuai dengan nilai model.

Model dalam aplikasi adalah instance DefaultBoundedRangeModel(), yang melacak nilai integer terbatas — dalam hal ini dari 0to 100— dengan metode berikut:

  • int getMaximum()
  • int getMinimum()
  • int getValue()
  • boolean getValueIsAdjusting()
  • int getExtent()
  • void setMaximum(int)
  • void setMinimum(int)
  • void setValue(int)
  • void setValueIsAdjusting(boolean)
  • void setExtent(int)
  • void setRangeProperties(int value, int extent, int min, int max, boolean adjusting)
  • void addChangeListener(ChangeListener)
  • void removeChangeListener(ChangeListener)

Seperti yang ditunjukkan oleh dua metode terakhir yang tercantum di atas, contoh DefaultBoundedRangeModel()pendengar perubahan dukungan. Contoh 1 menunjukkan bagaimana aplikasi memanfaatkan fitur tersebut:

Contoh 1. Dua pengamat bereaksi terhadap perubahan model

import javax.swing. *; import javax.swing.event. *; import java.awt. *; import java.awt.event. *; import java.util. *; public class Test extends JFrame { private DefaultBoundedRangeModel model = new DefaultBoundedRangeModel (100,0,0,100); private JSlider slider = new JSlider ( model ); private JLabel readOut = JLabel baru ("100%"); pribadi ImageIcon image = new ImageIcon ("shortcake.jpg"); private ImageView imageView = new ImageView (gambar, model); Uji publik () {super ("Pola Desain Pengamat"); Container contentPane = getContentPane (); Panel JPanel = JPanel baru (); panel.add (JLabel baru ("Atur Ukuran Gambar:")); panel.add (slider); panel.add (readOut); contentPane.add (panel, BorderLayout.NORTH); contentPane.add (imageView, BorderLayout.CENTER);model.addChangeListener (ReadOutSynchronizer ()) baru; } public static void main (String args []) {Test test = new Test (); test.setBounds (100.100.400.350); test.show (); } kelas ReadOutSynchronizer mengimplementasikan ChangeListener {public void stateChanged (ChangeEvent e) {String s = Integer.toString (model.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }}} kelas ImageView extends JScrollPane {private JPanel panel = new JPanel (); private Dimension originalSize = new Dimension (); private Image originalImage; ikon ImageIcon pribadi; public ImageView (ikon ImageIcon, model BoundedRangeModel) {panel.setLayout (new BorderLayout ()); panel.add (JLabel baru (icon)); this.icon = icon; this.originalImage = icon.getImage (); setViewportView (panel);model.addChangeListener (new ModelListener ()); originalSize.width = icon.getIconWidth (); originalSize.height = icon.getIconHeight (); } kelas ModelListener mengimplementasikan ChangeListener {public void stateChanged (ChangeEvent e) {BoundedRangeModel model = (BoundedRangeModel) e.getSource () ; if (model.getValueIsAdjusting ()) {int min = model.getMinimum (), max = model.getMaximum (), span = max - min, value = model.getValue (); pengganda ganda = (ganda) nilai / (ganda) rentang; multiplier = multiplier == 0,0? 0,01: pengali; Image scaled = originalImage.getScaledInstance ((int) (originalSize.width * multiplier), (int) (originalSize.height * multiplier), Image.SCALE_FAST); icon.setImage (berskala); panel.revalidate (); panel.repaint (); }}}}

Saat Anda menggerakkan kenop penggeser, penggeser mengubah nilai modelnya. Perubahan itu memicu notifikasi peristiwa ke dua pemroses perubahan yang terdaftar dengan model, yang menyesuaikan pembacaan dan menskalakan gambar. Kedua pendengar menggunakan peristiwa perubahan yang diteruskan ke

stateChanged()

untuk menentukan nilai baru model.

Swing adalah pengguna berat pola Observer — ini mengimplementasikan lebih dari 50 event listener untuk mengimplementasikan perilaku khusus aplikasi, mulai dari bereaksi terhadap tombol yang ditekan hingga memveto event penutupan jendela untuk frame internal. Tapi Swing bukan satu-satunya framework yang menggunakan pola Observer dengan baik — ini banyak digunakan di Java 2 SDK; misalnya: Abstrak Window Toolkit, framework JavaBeans, javax.namingpaket, dan penangan input / output.

Contoh 1 secara khusus menunjukkan penggunaan pola Observer dengan Swing. Sebelum kita membahas lebih lanjut detail pola Observer, mari kita lihat bagaimana pola tersebut diterapkan secara umum.

Bagaimana pola Observer bekerja

Gambar 2 menunjukkan bagaimana objek dalam pola Observer terkait.

Subjek, yang merupakan sumber peristiwa, memelihara kumpulan pengamat dan menyediakan metode untuk menambah dan menghapus pengamat dari koleksi itu. Subjek juga menerapkan notify()metode yang memberi tahu setiap pengamat terdaftar tentang peristiwa yang menarik bagi pengamat. Subjek memberi tahu pengamat dengan menggunakan metode pengamat update().

Gambar 3 menunjukkan diagram urutan untuk pola Observer.

Biasanya, beberapa objek yang tidak terkait akan memanggil metode subjek yang mengubah status subjek. Ketika itu terjadi, subjek memanggil notify()metodenya sendiri , yang mengulangi kumpulan pengamat, memanggil update()metode masing-masing pengamat .

Pola Observer adalah salah satu pola desain yang paling mendasar karena memungkinkan objek yang sangat terpisah untuk berkomunikasi. Dalam Contoh 1, satu-satunya hal yang diketahui model rentang terbatas tentang pendengarnya adalah bahwa mereka mengimplementasikan sebuah stateChanged()metode. Pendengar hanya tertarik pada nilai model, bukan bagaimana model diimplementasikan. Model dan pendengarnya hanya tahu sedikit tentang satu sama lain, tetapi berkat pola Observer, mereka dapat berkomunikasi. Tingkat pemisahan yang tinggi antara model dan pendengar memungkinkan Anda membangun perangkat lunak yang terdiri dari objek yang dapat dicolokkan, membuat kode Anda sangat fleksibel dan dapat digunakan kembali.

Java 2 SDK dan pola Observer

Java 2 SDK menyediakan implementasi klasik pola Observer dengan Observerantarmuka dan Observablekelas dari java.utildirektori. The Observablekelas merupakan subjek; pengamat mengimplementasikan Observerantarmuka. Menariknya, implementasi pola Observer klasik ini jarang digunakan dalam praktik karena membutuhkan mata pelajaran untuk memperluas Observablekelas. Mewajibkan pewarisan dalam kasus ini adalah desain yang buruk karena berpotensi semua jenis objek adalah kandidat subjek, dan karena Java tidak mendukung pewarisan ganda; seringkali, kandidat subjek tersebut sudah memiliki kelas super.

Implementasi berbasis peristiwa dari pola Observer, yang digunakan dalam contoh sebelumnya, adalah pilihan yang sangat banyak untuk implementasi pola Observer karena tidak memerlukan subjek untuk memperluas kelas tertentu. Sebaliknya, subjek mengikuti konvensi yang memerlukan metode pendaftaran pendengar publik berikut:

  • void addXXXListener(XXXListener)
  • void removeXXXListener(XXXListener)

Setiap kali properti terikat subjek (properti yang telah diamati oleh pendengar) berubah, subjek mengulangi pemrosesnya dan memanggil metode yang ditentukan oleh XXXListenerantarmuka.

Sekarang Anda harus memiliki pemahaman yang baik tentang pola Observer. Sisa artikel ini berfokus pada beberapa poin penting dari pola Observer.

Kelas batin anonim

Dalam Contoh 1, saya menggunakan kelas-kelas dalam untuk mengimplementasikan pendengar aplikasi, karena kelas pendengar digabungkan erat ke kelas yang melingkupinya; namun, Anda dapat menerapkan pendengar dengan cara apa pun yang Anda inginkan. Salah satu pilihan paling populer untuk menangani kejadian antarmuka pengguna adalah kelas dalam anonim, yang merupakan kelas tanpa nama yang dibuat secara in-line, seperti yang ditunjukkan pada Contoh 2:

Contoh 2. Menerapkan pengamat dengan kelas dalam anonim

... public class Test extends JFrame { ... public Test() { ... model.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { String s = Integer.toString(model.getValue()); readOut.setText(s + "%"); readOut.revalidate(); } }); } ... } class ImageView extends JScrollPane { ... public ImageView(final ImageIcon icon, BoundedRangeModel model) { ... model.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { BoundedRangeModel model = (BoundedRangeModel)e.getSource(); if(model.getValueIsAdjusting()) { int min = model.getMinimum(), max = model.getMaximum(), span = max - min, value = model.getValue(); double multiplier = (double)value / (double)span; multiplier = multiplier == 0.0 ? 0.01 : multiplier; Image scaled = originalImage.getScaledInstance( (int)(originalSize.width * multiplier), (int)(originalSize.height * multiplier), Image.SCALE_FAST); icon.setImage(scaled); panel.revalidate(); } } }); } } 

Example 2's code is functionally equivalent to Example 1's code; however, the code above uses anonymous inner classes to define the class and create an instance in one fell swoop.

JavaBeans event handler

Menggunakan kelas dalam anonim seperti yang ditunjukkan pada contoh sebelumnya sangat populer di kalangan pengembang, jadi dimulai dengan Platform Java 2, Edisi Standar (J2SE) 1.4, spesifikasi JavaBeans telah mengambil tanggung jawab untuk mengimplementasikan dan membuat contoh kelas dalam tersebut untuk Anda dengan EventHandlerkelas tersebut, seperti yang ditunjukkan pada Contoh 3:

Contoh 3. Menggunakan java.beans.EventHandler

import java.beans.EventHandler; ... public class Test extends JFrame {... public Test () {... model.addChangeListener (EventHandler.create (ChangeListener.class, ini, "updateReadout")); } ... public void updateReadout () {String s = Integer.toString (model.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }} ...