Observer dan Observable

Inilah masalahnya: Anda sedang merancang program yang akan membuat data yang menggambarkan pemandangan tiga dimensi dalam dua dimensi. Program harus modular dan harus mengizinkan beberapa tampilan secara bersamaan dari pemandangan yang sama. Setiap tampilan harus dapat menampilkan pemandangan dari sudut pandang yang berbeda, dalam kondisi pencahayaan yang berbeda. Lebih penting lagi, jika ada bagian dari adegan yang mendasarinya berubah, tampilan harus diperbarui sendiri.

Tak satu pun dari persyaratan ini menghadirkan tantangan pemrograman yang tidak dapat diatasi. Jika kode yang menangani setiap persyaratan harus ditulis de novo , bagaimanapun, itu akan menambah pekerjaan yang signifikan untuk keseluruhan upaya. Untungnya, dukungan untuk tugas-tugas ini sudah disediakan oleh perpustakaan kelas Java dalam bentuk antarmuka Observerdan kelas Observable--keduanya diinspirasi, sebagian, oleh persyaratan arsitektur MVC.

Arsitektur Model / View / Controller (MVC)

Arsitektur Model / View / Controller diperkenalkan sebagai bagian dari Smalltalk, bahasa pemrograman berorientasi objek populer yang ditemukan oleh Alan Kay. MVC dirancang untuk mengurangi upaya pemrograman yang diperlukan untuk membangun sistem yang menggunakan banyak presentasi tersinkronisasi dari data yang sama. Karakteristik utamanya adalah bahwa model, pengontrol, dan tampilan diperlakukan sebagai entitas terpisah, dan perubahan yang dibuat pada model harus tercermin secara otomatis di setiap tampilan.

Selain contoh program yang dijelaskan dalam paragraf pembuka di atas, arsitektur Model / View / Controller dapat digunakan untuk proyek seperti berikut ini:

  • Paket grafik yang berisi tampilan diagram batang, diagram garis, dan diagram lingkaran dari data yang sama.
  • Sistem CAD, di mana bagian-bagian desain dapat dilihat pada perbesaran yang berbeda, pada jendela yang berbeda, dan pada skala yang berbeda.

Gambar 1 mengilustrasikan arsitektur MVC dalam bentuknya yang paling umum. Ada satu model. Beberapa pengontrol memanipulasi model; beberapa tampilan menampilkan data dalam model, dan berubah saat status model berubah.

Gambar 1. Arsitektur Model / View / Controller

Manfaat MVC

Arsitektur Model / View / Controller memiliki beberapa keuntungan:

  • Ada pemisahan yang jelas antara komponen program - masalah di setiap domain dapat diselesaikan secara independen.
  • Ada API yang ditentukan dengan baik - apa pun yang menggunakan API dengan benar dapat menggantikan model, tampilan, atau pengontrol.
  • Pengikatan antara model dan tampilan bersifat dinamis - ini terjadi pada waktu proses, bukan pada waktu kompilasi.

Dengan menggabungkan arsitektur MVC ke dalam desain, bagian dari program dapat dirancang secara terpisah (dan dirancang untuk melakukan tugasnya dengan baik) dan kemudian diikat bersama pada saat dijalankan. Jika suatu komponen kemudian dianggap tidak sesuai, itu dapat diganti tanpa mempengaruhi bagian lainnya. Bandingkan skenario itu dengan pendekatan monolitik yang khas dari banyak program Java quick-and-dirty. Seringkali bingkai berisi semua status, menangani semua peristiwa, melakukan semua penghitungan, dan menampilkan hasilnya. Jadi, dalam semua sistem kecuali yang paling sederhana, membuat perubahan setelah fakta bukanlah hal yang sepele.

Mendefinisikan bagian-bagiannya

Model adalah objek yang merepresentasikan data dalam program. Ini mengelola data dan melakukan semua transformasi pada data itu. Model tidak memiliki pengetahuan khusus tentang pengontrol atau tampilan - model tersebut tidak berisi referensi internal juga. Sebaliknya, sistem itu sendiri mengambil tanggung jawab untuk memelihara hubungan antara model dan pandangannya dan memberi tahu pandangan ketika model berubah.

View adalah objek yang mengelola tampilan visual dari data yang direpresentasikan oleh model. Ini menghasilkan representasi visual dari objek model dan menampilkan data kepada pengguna. Ini berinteraksi dengan model melalui referensi ke objek model itu sendiri.

Pengontrol adalah objek yang menyediakan sarana untuk interaksi pengguna dengan data yang direpresentasikan oleh model. Ini menyediakan cara yang digunakan untuk membuat perubahan, baik pada informasi dalam model atau tampilan tampilan. Ini berinteraksi dengan model melalui referensi ke objek model itu sendiri.

Pada titik ini, contoh konkret mungkin bisa membantu. Pertimbangkan sebagai contoh sistem yang dijelaskan dalam pendahuluan.

Gambar 2. Sistem visualisasi tiga dimensi

Bagian utama dari sistem ini adalah model pemandangan tiga dimensi. Model adalah deskripsi matematis dari simpul dan wajah yang menyusun pemandangan. Data yang mendeskripsikan setiap sudut atau wajah dapat dimodifikasi (mungkin sebagai hasil dari masukan pengguna atau distorsi adegan atau algoritme morphing). Namun, tidak ada pengertian tentang sudut pandang, metode tampilan (wireframe atau solid), perspektif, atau sumber cahaya. Model adalah representasi murni dari elemen-elemen yang membentuk pemandangan.

Bagian program yang mengubah data dalam model menjadi tampilan grafis adalah tampilan. Pemandangan tersebut mewujudkan tampilan adegan yang sebenarnya. Ini adalah representasi grafis dari pemandangan dari sudut pandang tertentu, dalam kondisi pencahayaan tertentu.

Pengontrol mengetahui apa yang dapat dilakukan pada model, dan mengimplementasikan antarmuka pengguna yang memungkinkan tindakan tersebut dimulai. Dalam contoh ini, panel kontrol entri data memungkinkan pengguna untuk menambah, mengubah, atau menghapus simpul dan wajah.

Observer dan Observable

Bahasa Java mendukung arsitektur MVC dengan dua kelas:

  • Observer: Objek apa pun yang ingin diberi tahu saat status objek lain berubah.
  • Observable: Objek apa pun yang negaranya mungkin menarik, dan di mana objek lain dapat mendaftarkan minat.

Kedua kelas ini dapat digunakan untuk mengimplementasikan lebih dari sekedar arsitektur MVC. Mereka cocok untuk sistem apa pun di mana objek perlu diberi tahu secara otomatis tentang perubahan yang terjadi pada objek lain.

Typically, the model is a subtype of Observable and the view is a subtype of Observer. These two classes handle MVC's automatic notification function. They provide the mechanism by which the views can be automatically notified of changes in the model. Object references to the model in both the controller and the view allow access to data in the model.

Observer and Observable functions

The following are code listings for the observer and observable functions:

Observer

  • public void update(Observable obs, Object obj)

    Called when a change has occurred in the state of the observable.

Observable

  • public void addObserver(Observer obs)

    Adds an observer to the internal list of observers.

  • public void deleteObserver(Observer obs)

    Deletes an observer from the internal list of observers.

  • public void deleteObservers()

    Deletes all observers from the internal list of observers.

  • public int countObservers()

    Returns the number of observers in the internal list of observers.

  • protected void setChanged()

    Sets the internal flag that indicates this observable has changed state.

  • protected void clearChanged()

    Clears the internal flag that indicates this observable has changed state.

  • public boolean hasChanged()

    Returns the boolean value true if this observable has changed state.

  • public void notifyObservers()

    Checks the internal flag to see if the observable has changed state and notifies all observers.

  • public void notifyObservers(Object obj)

    Checks the internal flag to see if the observable has changed state and notifies all observers. Passes the object specified in the parameter list to the notify() method of the observer.

Next we'll take a look at how to create a new Observable and Observer class, and how to tie the two together.

Extend an observable

A new class of observable objects is created by extending class Observable. Because class Observable already implements all of the methods necessary to provide the desired behavior, the derived class need only provide some mechanism for adjusting and accessing the internal state of the observable object.

In the ObservableValue listing below, the internal state of the model is captured by the integer n. This value is accessed (and, more importantly, modified) only through public accessors. If the value is changed, the observable object invokes its own setChanged() method to indicate that the state of the model has changed. It then invokes its own notifyObservers() method in order to update all of the registered observers.

Listing 1. ObservableValue

 import java.util.Observable; public class ObservableValue extends Observable { private int n = 0; public ObservableValue(int n) { this.n = n; } public void setValue(int n) { this.n = n; setChanged(); notifyObservers(); } public int getValue() { return n; } } 

Implement an observer

A new class of objects that observe the changes in state of another object is created by implementing the Observer interface. The Observer interface requires that an update() method be provided in the new class. The update() method is called whenever the observable changes state and announces this fact by calling its notifyObservers() method. The observer should then interrogate the observable object to determine its new state, and, in the case of the MVC architecture, adjust its view appropriately.

In the following TextObserver listing, the notify() method first checks to ensure that the observable that has announced an update is the observable that this observer is observing. If it is, it then reads the observable's state, and prints the new value.

Listing 2. TextObserver

 import java.util.Observer; import java.util.Observable; public class TextObserver implements Observer { private ObservableValue ov = null; public TextObserver(ObservableValue ov) { this.ov = ov; } public void update(Observable obs, Object obj) { if (obs == ov) { System.out.println(ov.getValue()); } } } 

Tie the two together

A program notifies an observable object that an observer wishes to be notified about changes in its state by calling the observable object's addObserver() method. The addObserver() method adds the observer to the internal list of observers that should be notified if the state of the observable changes.

The example below, showing class Main, demonstrates how to use the addObserver() method to add an instance of the TextObserver class (Listing 2) to the observable list maintained by the ObservableValue class (Listing 1).

Listing 3. addObserver()

 public class Main { public Main() { ObservableValue ov = new ObservableValue(0); TextObserver to = new TextObserver(ov); ov.addObserver(to); } public static void main(String [] args) { Main m = new Main(); } } 

How it all works together

The following sequence of events describes how the interaction between an observable and an observer typically occurs within a program.

  1. First the user manipulates a user interface component representing a controller. The controller makes a change to the model via a public accessor method -- which is setValue() in the example above.
  2. The public accessor method modifies the private data, adjusts the internal state of the model, and calls its setChanged() method to indicate that its state has changed. It then calls notifyObservers() to notify the observers that it has changed. The call to notifyObservers() could also be performed elsewhere, such as in an update loop running in another thread.
  3. The update() methods on each of the observers are called, indicating that a change in state has occurred. The observers access the model's data via the model's public accessor methods and update their respective views.

Observer/Observable in an MVC architecture

Now let's consider an example demonstrating how observables and observers typically work together in an MVC architecture. Like the model in the ObservableValue (Listing 1) the model in this example is very simple. Its internal state consists of a single integer value. The state is manipulated exclusively via accessor methods like those in ObservableValue. The code for the model is found here.

Initially, a simple text view/controller class was written. The class combines the features of both a view (it textually displays the value of the current state of the model) and a controller (it allows the user to enter a new value for the state of the model). The code is found here.

By designing the system using the MVC architecture (rather than embedding the code for the model, the view, and the text controller in one monolithic class), the system is easily redesigned to handle another view and another controller. In this case, a slider view/controller class was written. The position of the slider represents the value of the current state of the model and can be adjusted by the user to set a new value for the state of the model. The code is found here.

About the author

Todd Sundsted has been writing programs since computers became available in desktop models. Though originally interested in building distributed object applications in C++, Todd moved to the Java programming language when Java became the obvious choice for that sort of thing.

This story, "Observer and Observable" was originally published by JavaWorld .