Pandangan dalaman Observer

Tidak lama dahulu kopling saya sudah habis, jadi Jeep saya ditarik ke dealer tempatan. Saya tidak mengenali sesiapa di kedai itu, dan tidak ada yang mengenali saya, jadi saya memberikan nombor telefon mereka supaya mereka dapat memberitahu saya dengan anggaran. Pengaturan itu berjalan dengan baik dan kami melakukan perkara yang sama ketika kerja itu selesai. Kerana ini semua berlaku dengan sempurna, saya mengesyaki bahagian perkhidmatan di peniaga menggunakan corak yang sama dengan kebanyakan pelanggannya.

Corak penerbitan-langganan ini, di mana pemerhati mendaftar dengan subjek dan kemudian menerima pemberitahuan , adalah perkara biasa, baik dalam kehidupan seharian dan di dunia maya pengembangan perisian. Sebenarnya, corak Observer , seperti yang diketahui, adalah salah satu kunci pengembangan perisian berorientasikan objek kerana memungkinkan objek yang berbeza berkomunikasi. Keupayaan itu membolehkan anda memasukkan objek ke dalam kerangka pada waktu runtime, yang memungkinkan untuk perisian yang sangat fleksibel, dapat diperluas, dan dapat digunakan kembali.

Catatan: Anda boleh memuat turun kod sumber artikel ini dari Sumber.

Corak Pemerhati

Dalam Corak Reka Bentuk , penulis menerangkan corak Pemerhati seperti ini:

Tentukan kebergantungan satu ke banyak antara objek sehingga apabila satu objek berubah keadaan, semua tanggungannya diberitahu dan dikemas kini secara automatik.

Corak Pemerhati mempunyai satu subjek dan berpotensi banyak pemerhati. Pemerhati mendaftar dengan subjek, yang memberitahu pemerhati apabila peristiwa berlaku. Contoh prototaip Observer adalah antara muka pengguna grafik (GUI) yang secara serentak memaparkan dua paparan satu model; pandangan mendaftar dengan model, dan apabila model berubah, ia memberitahu pandangan, yang akan diperbaharui dengan sewajarnya. Mari lihat bagaimana ia berfungsi.

Pemerhati beraksi

Aplikasi yang ditunjukkan dalam Rajah 1 mengandungi satu model dan dua pandangan. Nilai model, yang mewakili pembesaran gambar, dimanipulasi dengan menggerakkan tombol gelangsar. Pandangan, yang dikenal sebagai komponen di Swing, adalah label yang menunjukkan nilai model dan panel tatal yang menskalakan gambar sesuai dengan nilai model.

Model dalam permohonan itu adalah satu contoh DefaultBoundedRangeModel(), yang menjejaki integer nilai dalam terbatas kes ini dari 0untuk 100-dengan kaedah ini:

  • 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 kaedah terakhir yang disenaraikan di atas, contoh DefaultBoundedRangeModel()sokongan mengubah pendengar. Contoh 1 menunjukkan bagaimana aplikasi memanfaatkan ciri tersebut:

Contoh 1. Dua pemerhati bertindak balas terhadap perubahan model

import javax.swing. *; import javax.swing.event. *; import java.awt. *; import java.awt.event. *; import java.util. *; Ujian kelas awam meluaskan model JFrame { private DefaultBoundedRangeModel = baru DefaultBoundedRangeModel (100,0,0,100); slaid JSlider peribadi = JSlider baru ( model ); JLabel peribadi readOut = JLabel baru ("100%"); gambar ImageIcon peribadi = ImageIcon baru ("shortcake.jpg"); ImageView peribadiViewView = ImageView baru (gambar, model); ujian awam () {super ("Corak Reka Bentuk Pemerhati"); Container contentPane = getContentPane (); Panel JPanel = JPanel baru (); panel.add (JLabel baru ("Tetapkan Saiz Gambar:")); panel.add (gelangsar); panel.add (readOut); contentPane.add (panel, BorderLayout.NORTH); contentPane.add (imageView, BorderLayout.CENTRE);model.addChangeListener (ReadOutSynchronizer baru ()); } public static void main (String args []) {Uji ujian = Ujian baru (); 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 meluaskan JScrollPane {panel JPanel peribadi = JPanel baru (); Dimensi peribadi originalSize = Dimensi baru (); gambar peribadi originalImage; ikon ImageIcon peribadi; awam ImageView (ikon ImageIcon, model BoundedRangeModel) {panel.setLayout (BorderLayout baru ()); panel.add (JLabel baru (ikon)); ikon ini.icon =; this.originalImage = icon.getImage (); setViewportView (panel);model.addChangeListener (ModelListener baru ()); originalSize.width = icon.getIconWidth (); originalSize.height = icon.getIconHeight (); } class ModelListener menerapkan ChangeListener {public void stateChanged (ChangeEvent e) {BoundedRangeModel model = (BoundedRangeModel) e.getSource () ; jika (model.getValueIsAdjusting ()) {int min = model.getMinimum (), max = model.getMaximum (), span = max - min, nilai = model.getValue (); pengganda berganda = (double) nilai / (double) span; pengganda = pengganda == 0.0? 0.01: pengganda; Imej berskala = originalImage.getScaledInstance ((int) (originalSize.width * multiplier), (int) (originalSize.height * pengganda), Image.SCALE_FAST); icon.setImage (skala); panel.revalidate (); panel.repaint (); }}}}

Apabila anda menggerakkan tombol gelangsar, gelangsar mengubah nilai modelnya. Perubahan itu mencetuskan pemberitahuan peristiwa kepada dua pendengar perubahan yang didaftarkan dengan model, yang menyesuaikan pembacaan dan skala gambar. Kedua pendengar menggunakan peristiwa perubahan yang diteruskan

stateChanged()

untuk menentukan nilai baru model.

Swing adalah pengguna berat dari pola Observer - ini menerapkan lebih dari 50 pendengar acara untuk menerapkan tingkah laku khusus aplikasi, dari bereaksi ke butang yang ditekan hingga memveto acara tutup jendela untuk bingkai dalaman. Tetapi Swing bukan satu-satunya kerangka kerja yang menjadikan pola Observer digunakan dengan baik - ia digunakan secara meluas dalam Java 2 SDK; sebagai contoh: Abstract Window Toolkit, kerangka JavaBeans, javax.namingpaket, dan pengendali input / output.

Contoh 1 secara khusus menunjukkan penggunaan corak Observer dengan Swing. Sebelum kita membincangkan lebih banyak perincian corak Pemerhati, mari kita lihat bagaimana corak ini dilaksanakan secara umum.

Bagaimana corak Pemerhati berfungsi

Rajah 2 menunjukkan bagaimana objek dalam corak Pemerhati berkaitan.

Subjek, yang merupakan sumber peristiwa, mengekalkan koleksi pemerhati dan menyediakan kaedah untuk menambah dan mengeluarkan pemerhati dari koleksi tersebut. Subjek juga menerapkan notify()kaedah yang memberitahu setiap pemerhati yang berdaftar mengenai peristiwa yang menarik perhatian pemerhati. Subjek memberitahu pemerhati dengan menggunakan kaedah pemerhati update().

Rajah 3 menunjukkan gambarajah turutan bagi corak Pemerhati.

Biasanya, beberapa objek yang tidak berkaitan akan menggunakan kaedah subjek yang mengubah keadaan subjek. Apabila itu berlaku, subjek menggunakan notify()kaedahnya sendiri , yang mengulangi pengumpulan pemerhati, memanggil update()kaedah setiap pemerhati .

Corak Observer adalah salah satu corak reka bentuk yang paling asas kerana membolehkan objek yang sangat dipisahkan berkomunikasi. Dalam Contoh 1, satu-satunya perkara yang diketahui oleh model range terikat tentang pendengarnya ialah mereka menerapkan stateChanged()kaedah. Pendengar hanya berminat dengan nilai model, bukan bagaimana model itu dilaksanakan. Model dan pendengarnya tidak banyak mengetahui tentang satu sama lain, tetapi berkat corak Observer, mereka dapat berkomunikasi. Tahap pemutusan antara model dan pendengar yang tinggi itu membolehkan anda membina perisian yang terdiri daripada objek yang boleh dipasang, menjadikan kod anda sangat fleksibel dan dapat digunakan semula.

SDK Java 2 dan corak Pemerhati

Java 2 SDK menyediakan pelaksanaan klasik corak Observer dengan Observerantara muka dan Observablekelas dari java.utildirektori. The Observablekelas mewakili subjek; pemerhati melaksanakan Observerantara muka. Menariknya, pelaksanaan corak Observer klasik ini jarang digunakan dalam praktik kerana memerlukan subjek untuk melanjutkan Observablekelas. Memerlukan pewarisan dalam hal ini adalah reka bentuk yang buruk kerana kemungkinan semua jenis objek adalah calon subjek, dan kerana Java tidak menyokong banyak warisan; selalunya, calon subjek sudah mempunyai superclass.

The event-based implementation of the Observer pattern, which was used in the preceding example, is the overwhelming choice for Observer pattern implementation because it doesn't require subjects to extend a particular class. Instead, subjects follow a convention that requires the following public listener registration methods:

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

Whenever a subject's bound property (a property that's been observed by listeners) changes, the subject iterates over its listeners and invokes the method defined by the XXXListener interface.

By now you should have a good grasp of the Observer pattern. The rest of this article focuses on some of the Observer pattern's finer points.

Anonymous inner classes

In Example 1, I used inner classes to implement the application's listeners, because the listener classes were tightly coupled to their enclosing class; however, you can implement listeners any way you desire. One of the most popular choices for handling user interface events is the anonymous inner class, which is a class with no name that's created in-line, as demonstrated in Example 2:

Example 2. Implement observers with anonymous inner classes

... 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 dalaman tanpa nama seperti yang ditunjukkan pada contoh sebelumnya adalah sangat popular di kalangan pembangun, jadi bermula dengan Platform Java 2, Edisi Standard (J2SE) 1.4, spesifikasi JavaBeans telah mengambil tanggungjawab untuk melaksanakan dan memberi contoh kepada kelas tersebut untuk anda EventHandler, seperti yang ditunjukkan dalam Contoh 3:

Contoh 3. Menggunakan java.beans.EventHandler

import java.beans.EventHandler; ... Ujian kelas awam memanjangkan JFrame {... ujian awam () {... model.addChangeListener (EventHandler.create (ChangeListener.class, this, "updateReadout")); } ... pembaharuan awam terbatalReadout () {String s = Integer.toString (model.getValue ()); readOut.setText (s + "%"); readOut.revalidate (); }} ...