Implementasi pola pengamat sederhana
Copy kode kodenya sebagai berikut:
impor java.util.ArrayList;
impor java.util.Koleksi;
impor java.util.Iterator;
impor java.util.List;
/**
* Callback digunakan dalam mode pengamat:
* A. Pengamat mendaftarkan dirinya ke daftar pendengar yang diamati, dan kelas pengamat itu sendiri menyediakan fungsi panggilan balik
* B. Observable (Observable atau Subject) menyimpan daftar pengamat dan dapat mendaftarkan dan membatalkan pendaftaran pengamat
* C. Setelah status pengamat berubah, ia dapat memanggil notifyObservers(). Metode ini akan melintasi daftar pengamat dan memanggilnya satu per satu.
fungsi panggilan balik yang disediakan oleh pengamat
* @penulis akan
*
*/
kelas publik SimpleObserverPattern {
public static void main(String[] args) {
SimpleObserverPattern sop = new SimpleObserverPattern();
Daftar<IObserver> pengamat = new ArrayList<IObserver>();
IObserver ObserverA = sop.new Observer("ObserverA");
IObserver pengamatB = sop.pengamat baru("ObserverB");
pengamat.tambahkan(pengamatA);
pengamat.tambahkan(pengamatB);
IObservable observable = sop.new Observable(pengamat);
observable.registerObserver(sop.new Observer("ObserverC"));
dapat diamati.changeState();
dapat diamati.close();
}
// Orang yang diamati disebut Subjek di beberapa tempat.
antarmuka IObservable {
void registerObserver(pengamat IObserver);
void unregisterObserver(pengamat IObserver);
batal beri tahuObservers();
String getState();
batalkan perubahanState();
batal tutup();
}
class Observable mengimplementasikan IObservable {
private static final String BARU = "Baru";
private static final String CHANGED = "Berubah";
private static final String CLOSED = "Tutup";
status String pribadi;
pengamat Daftar<IObserver> pribadi;
publik Dapat Diamati() {
ini(batal);
}
public Observable(Daftar<IObserver> pengamat) {
if(pengamat == null) {
pengamat = new ArrayList<IObserver>();
}
this.observers = Koleksi.synchronizedList(observer);
this.state = BARU;
}
@Mengesampingkan
public void registerObserver(pengamat IObserver) {
pengamat.add(pengamat);
}
@Mengesampingkan
public void unregisterObserver(pengamat IObserver) {
pengamat.hapus(pengamat);
}
@Mengesampingkan
kekosongan publik notifyObservers() {
Iterator<IObserver> iter = pengamat.iterator();
while(iter.hasNext()) {
iter.next().update(ini);
}
}
@Mengesampingkan
String publik getState() {
negara bagian kembali;
}
@Mengesampingkan
kekosongan publik changeState() {
this.state = BERUBAH;
beri tahuObserver();
}
@Mengesampingkan
kekosongan publik tutup() {
this.state = TUTUP;
beri tahuObserver();
}
}
antarmuka IObserver {
batalkan pembaruan(pengamatan yang dapat diobservasi);
}
kelas Pengamat mengimplementasikan IObserver {
nama String pribadi;
Pengamat publik (Nama string) {
ini.nama = nama;
}
@Mengesampingkan
pembaruan kekosongan publik(IObservable observalbe) {
Sistem.keluar.println(
String.format("%s menerima perubahan observalbe, status observalbe saat ini adalah %s",
nama, observalbe.getState()));
}
}
}
Implementasi di atas secara langsung menggunakan objek yang diamati sebagai parameter fungsi panggilan balik. Ini sangat tidak elegan dan mungkin berfungsi dalam skenario sederhana.
Namun kenyataannya, dalam banyak kasus, orang yang diamati memiliki banyak peristiwa atau keadaan, dan setiap pengamat mungkin tertarik pada peristiwa atau keadaan yang berbeda, atau untuk tujuan menyembunyikan informasi, kami tidak ingin setiap pengamat dapat Mengakses semua keadaan. di dalam Yang Dapat Diamati.
Dengan cara ini, saya terus mengembangkan kode ke versi berikutnya. Perhatikan bahwa saya tidak mempertimbangkan masalah konkurensi secara detail di sini.
Copy kode kodenya sebagai berikut:
impor java.util.Koleksi;
impor java.util.HashSet;
import java.util.Hashtable;
impor java.util.Iterator;
impor java.util.Set;
kelas publik MultiEventObserverPattern {
public static void main(String[] args) {
MultiEventObserverPattern meop = MultiEventObserverPattern baru();
IObservable dapat diamati = meop.new Observable();
IObserver pengamatA = meop.new Observer("ObserverA");
IObserver pengamatB = meop.new Observer("ObserverB");
//Daftarkan acara menarik
dapat diobservasi.registerObserver(dapat diobservasi.getEventA(), pengamatA);
dapat diobservasi.registerObserver(dapat diobservasi.getEventB(), pengamatB);
//Ubah keadaan yang diamati
dapat diamati.changeStateA();
dapat diamati.changeStateB();
}
antarmuka IEvent {
batal acaraPerubahan();
String getState();
}
kelas EventA mengimplementasikan IEvent {
private static final String INITIALIZED = "Diinisialisasi";
String akhir statis pribadi PENDING = "Tertunda";
status String pribadi;
Acara publikA() {
this.state = DIINISIalisasi;
}
@Mengesampingkan
kekosongan publik eventChange() {
System.out.println("Acara Perubahan");
this.state = TERTUNDA;
}
@Mengesampingkan
String publik keString() {
kembalikan "AcaraA";
}
@Mengesampingkan
String publik getState() {
negara bagian kembali;
}
}
kelas EventB mengimplementasikan IEvent {
private static final String BARU = "Baru";
private static final String IDLE = "Idle";
status String pribadi;
Acara publikB() {
this.state = BARU;
}
@Mengesampingkan
kekosongan publik eventChange() {
System.out.println("Perubahan EventB");
this.state = IDLE;
}
@Mengesampingkan
String publik keString() {
kembalikan "AcaraB";
}
@Mengesampingkan
String publik getState() {
negara bagian kembali;
}
}
// Dapat diamati, terkadang disebut Subjek
antarmuka IObservable {
void registerObserver(acara IEvent, pengamat IObserver);
void unregisterObserver(acara IEvent, pengamat IObserver);
// Memberi tahu pengamat bahwa suatu peristiwa telah terjadi
void notifyObservers(acara IEvent);
batalkan perubahanStateA();
batalkan changeStateB();
IEvent getEventA();
IEvent getEventB();
}
class Observable mengimplementasikan IObservable {
acara IEvent pribadiA;
acara IEvent pribadiB;
private Hashtable<IEvent, Set<IObserver>> eventObserverMapping;
Dapat Diamati publik() {
ini(batal);
}
// Jika beberapa Set<IObserver> yang diteruskan oleh evenObserverMapping belum dimodifikasi secara sinkron, Anda tidak dapat melakukan apa pun untuk mengatasinya.
Dapat Diamati publik(Hashtable<IEvent, Set<IObserver>> eventObserverMapping) {
if(eventObserverMapping == null) {
eventObserverMapping = Hashtable baru<IEvent, Set<IObserver>> ();
}
this.eventObserverMapping = Hashtable baru<IEvent, Set<IObserver>> ();
this.eventA = EventA baru();
this.eventB = EventB baru();
}
@Mengesampingkan
public void registerObserver(peristiwa IEvent, pengamat IObserver) {
Setel<IObserver> pengamat = eventObserverMapping.get(event);
if(pengamat == null) {
pengamat = Koleksi.synchronizedSet(HashSet baru<IObserver> ());
pengamat.add(pengamat);
eventObserverMapping.put(event, pengamat);
}
kalau tidak {
pengamat.add(pengamat);
}
}
@Mengesampingkan
public void unregisterObserver(peristiwa IEvent, pengamat IObserver) {
Setel<IObserver> pengamat = eventObserverMapping.get(event);
if(pengamat != null) {
pengamat.hapus(pengamat);
}
}
@Mengesampingkan
public void notifyObservers(acara IEvent) {
Setel<IObserver> pengamat = eventObserverMapping.get(event);
if(pengamat != null && pengamat.ukuran() > 0) {
Iterator<IObserver> iter = pengamat.iterator();
while(iter.hasNext()) {
iter.next().update(acara);
}
}
}
@Mengesampingkan
kekosongan publik changeStateA() {
// Mengubah keadaan A akan memicu kejadian A
eventA.eventChange();
beri tahuObserver(eventA);
}
@Mengesampingkan
kekosongan publik changeStateB() {
// Mengubah keadaan B akan memicu kejadian B
eventB.eventChange();
beri tahuObserver(eventB);
}
@Mengesampingkan
IEvent publik getEventA() {
kembali acaraA;
}
@Mengesampingkan
IEvent publik getEventB() {
kembalikan acaraB;
}
}
antarmuka IObserver {
batalkan pembaruan (acara IEvent);
}
kelas Pengamat mengimplementasikan IObserver {
nama String pribadi;
Pengamat publik (Nama string) {
ini.nama = nama;
}
@Mengesampingkan
pembaruan kekosongan publik (acara IEvent) {
Sistem.keluar.println(
String.format("%s menerima perubahan %s, status observasi saat ini adalah %s",
nama, acara, acara.getState()));
}
}
}
Kelihatannya sempurna, tapi masih belum sempurna. Karena peristiwa dikodekan secara keras sebagai properti kelas Pengamat. Dengan cara ini, jenis acara ditentukan pada waktu kompilasi. Jika Anda ingin menambahkan jenis acara baru, Anda harus memodifikasi antarmuka IObservable dan kelas Observable, yang sangat mengurangi fleksibilitas.
Hal ini setara dengan pengamat yang digabungkan dengan peristiwa spesifik ini, jadi bagaimana kita mendobrak batasan ini?
Jawabannya adalah dengan memperkenalkan komponen baru dan membiarkan komponen tersebut mengelola hubungan antara peristiwa, pengamat, dan objek yang diamati. Ketika suatu peristiwa terjadi, komponen tersebut juga akan memanggil fungsi panggilan balik pengamat. Ini juga semacam pemisahan, agak mirip dengan wadah IOC Spring.
Untuk implementasi spesifiknya, menurut saya Guava EventBus telah melakukan tugasnya dengan cukup baik.
PS: Postingan ini bukan iklan Guava EventBus, melainkan ide saya sendiri yang dikembangkan selangkah demi selangkah, dan lambat laun sejalan dengan ide desain Guava EventBus.
Mari kita lanjutkan melihat contoh kelas standar JDK yang mengimplementasikan pola pengamat, lalu menganalisis implementasi kode sumbernya. Yang perlu kita lihat hanyalah kelas Observable dan antarmuka Observer.
Kelas standar JDK menerapkan pola pengamat
Copy kode kodenya sebagai berikut:
import java.util.Dapat Diamati;
impor java.util.Observer;
/**
* Menerapkan pola pengamat menggunakan kelas standar dalam paket java.util
* @penulis akan
*
*/
kelas publik JDKObserverDemo {
public static void main(String[] args) {
JDKObserverDemo jod = JDKObserverDemo baru();
// Pengamat
MyObservable myObservable = jod.new MyObservable("halo");
// Pengamat
Pengamat myObserver = jod.new MyObserver();
// mendaftar
myObservable.addObserver(myObservable);
//Ubah keadaan yang diamati dan picu fungsi callback pengamat
myObservable.setValue("akan");
}
kelas MyObservable memperluas Observable {
private String watchValue; // nilai yang diamati
publik MyObservable(String WatchValue) {
this.watchedValue = watchValue;
}
public void setValue(String nilai baru) {
if(!watchedValue.equals(newValue)) {
menyaksikanValue = Nilai baru;
setChanged();
notifyObservers(nilai baru);
}
}
@Mengesampingkan
String publik keString() {
kembalikan "Dapat Diobservasi Saya";
}
}
kelas MyObserver mengimplementasikan Pengamat {
@Mengesampingkan
pembaruan kekosongan publik(Dapat diamati o, Objek arg) {
System.out.println(o + "status berubah, argumennya adalah: " + arg);
}
}
}
Setelah melihat implementasi Observer dan Observable di perpustakaan standar JDK, ini sangat sederhana, jadi saya tidak ingin bicara lebih banyak.
Di bawah ini adalah implementasi pendengar di Quartz.
Pendengar QuartzScheduler
Copy kode kodenya sebagai berikut:
impor java.util.ArrayList;
import java.util.Date;
impor java.util.List;
/**
* Kelas inti kuarsa, setara dengan Observable (dapat diamati)
* @penulis akan
*
*/
Penjadwal Kuarsa kelas publik {
pribadi ArrayList<SchedulerListener> internalSchedulerListeners = ArrayList<SchedulerListener>(10);
//private ArrayList<JobListener> interanlJobListeners = new ArrayList<JobListener>(); // Sebuah Observable dapat berisi beberapa set pendengar
jadwal Tanggal publikPekerjaan(Pemicu pemicu) {
jika(pemicu == nol) {
kembalikan nol;
}
System.out.println("Jadwalkan pekerjaan, pemicu: " + pemicu);
notifySchedulerListenersScheduled(pemicu);
kembalikan Tanggal baru();
}
public void unScheduleJob(Pemicu pemicu) {
jika(pemicu == nol) {
kembali;
}
System.out.println("Batalkan jadwal pekerjaan, pemicu: " + pemicu);
notifyShedulerListenerUnScheduled(pemicu);
}
//Daftarkan PenjadwalListener
public void addInternalSchedulerListener(SchedulerListener schedulerListener) {
disinkronkan (internalSchedulerListeners) {
internalSchedulerListeners.add(schedulerListener);
}
}
// Hapus PenjadwalListener
boolean publik deleteInternalSchedulerListener(SchedulerListener schedulerListener) {
disinkronkan (internalSchedulerListeners) {
kembalikan internalSchedulerListeners.remove(schedulerListener);
}
}
Daftar publik<SchedulerListener> getInternalSchedulerListeners() {
disinkronkan (internalSchedulerListeners) {
return java.util.Collections.unmodifiedList(new ArrayList<SchedulerListener>(internalSchedulerListeners));
}
}
public void notifySchedulerListenersScheduled(Pemicu pemicu) {
untuk(pendengarSchedulerListener: getInternalSchedulerListeners()) {
pendengar.pekerjaanTerjadwal(pemicu);
}
}
public void notifyShedulerListenerUnScheduled(Pemicu pemicu) {
untuk(pendengarSchedulerListener: getInternalSchedulerListeners()) {
pendengar.pekerjaanTidak Terjadwal(pemicu);
}
}
}
Penjadwal Pendengar
Copy kode kodenya sebagai berikut:
// Antarmuka mendengarkan, fungsi panggilan balik, Klien perlu menyediakan implementasi fungsi panggilan balik saat mendaftar untuk pemantauan
antarmuka publik Penjadwal Pendengar {
void jobScheduled(Pemicu pemicu);
void jobUnScheduled(Pemicu pemicu);
}
Pemicu
Copy kode kodenya sebagai berikut:
//Pemicu
Pemicu kelas publik {
kunci pemicu String pribadi;
nama pemicu String pribadi;
Pemicu publik(String triggerKey, String triggerName) {
this.triggerKey = triggerKey;
this.triggerName = triggerName;
}
String publik getTriggerKey() {
kembalikan triggerKey;
}
public void setTriggerKey(String triggerKey) {
this.triggerKey = triggerKey;
}
String publik getTriggerName() {
kembalikan triggerName;
}
public void setTriggerName(String triggerName) {
this.triggerName = triggerName;
}
String publik keString() {
return String.format("{triggerKey: %s, triggerName: %s}", triggerKey, triggerName);
}
}
Tes
Copy kode kodenya sebagai berikut:
Tes kelas publik {
public static void main(String[] args) {
QuartzScheduler qs = QuartzScheduler baru();
Pendengar SchedulerListenerA = SchedulerListener baru() {
@Mengesampingkan
public void jobUnScheduled(Pemicu pemicu) {
System.out.println("pekerjaan pendengar tidak terjadwal: " + trigger.getTriggerName());
}
@Mengesampingkan
public void jobScheduled(Pemicu pemicu) {
System.out.println("pekerjaan pendengar dijadwalkan: " + trigger.getTriggerName());
}
};
PenjadwalListenerpendengarB = PenjadwalListenerbaru() {
@Mengesampingkan
public void jobUnScheduled(Pemicu pemicu) {
System.out.println("pekerjaan pendengarB tidak terjadwal: " + trigger.getTriggerName());
}
@Mengesampingkan
public void jobScheduled(Pemicu pemicu) {
System.out.println("pekerjaan pendengarB dijadwalkan: " + trigger.getTriggerName());
}
};
//Daftarkan Pendengar Penjadwal
qs.addInternalSchedulerListener(listenerA);
qs.addInternalSchedulerListener(listenerB);
Pemicu triggerA = Pemicu baru("Key1", "triggerA");
Pemicu triggerB = Pemicu baru("Key2", "triggerB");
qs.scheduleJob(triggerA);
qs.scheduleJob(triggerB);
qs.unScheduleJob(triggerA);
}
}