1. 概述
在本教程中,我們將描述觀察者模式,並探討一些 Java 實現替代方案。
2. 觀察者模式是什麼?
觀察者模式是一種行為設計模式。它指定對象之間的通信:可觀察對象和觀察者。 可觀察對象是一個通知觀察者其狀態發生變化的對象。
例如,一個新聞機構可以當接收到新聞時通知頻道。接收到新聞是新聞機構狀態發生變化,從而導致頻道被通知。
讓我們看看我們如何自己實現它。
首先,我們將定義NewsAgency類:
public class NewsAgency {
private String news;
private List<Channel> channels = new ArrayList<>();
public void addObserver(Channel channel) {
this.channels.add(channel);
}
public void removeObserver(Channel channel) {
this.channels.remove(channel);
}
public void setNews(String news) {
this.news = news;
for (Channel channel : this.channels) {
channel.update(this.news);
}
}
}NewsAgency 是一個可觀察對象,當新聞更新時,NewsAgency 的狀態會發生改變。當改變發生時,NewsAgency 會通過調用觀察者的update()方法通知它們。
為了能夠做到這一點,可觀察對象需要保持對觀察者的引用。 在我們的例子中,這就是channels變量。
現在讓我們看看觀察者,Channel類,可能是什麼樣的。它應該具有update()方法,該方法在NewsAgency的狀態發生改變時被調用:
public class NewsChannel implements Channel {
private String news;
@Override
public void update(Object news) {
this.setNews((String) news);
}
// standard getter and setter
}通道接口僅有一個方法:
public interface Channel {
public void update(Object o);
}現在,如果我們將 NewsChannel 實例添加到觀察者列表中,並更改 NewsAgency 的狀態,則 NewsChannel 實例將被更新:
NewsAgency observable = new NewsAgency();
NewsChannel observer = new NewsChannel();
observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");在 Java 核心庫中,存在一個預定義的 Observer 接口,這使得實現觀察者模式更加簡單。我們來了解一下它。
3. 使用 Observer 模式實現
java.util.Observer 接口定義了 update() 方法,因此我們無需像前一節中那樣自行定義它。
下面我們來看如何在我們的實現中使用它:
public class ONewsChannel implements Observer {
private String news;
@Override
public void update(Observable o, Object news) {
this.setNews((String) news);
}
// standard getter and setter
}
在這裏,第二個參數來自 Observable,正如我們稍後將看到的。
要定義 Observable,我們需要擴展 Java 的 Observable 類:
public class ONewsAgency extends Observable {
private String news;
public void setNews(String news) {
this.news = news;
setChanged();
notifyObservers(news);
}
}請注意,我們不需要直接調用觀察者的 update() 方法。我們只需要調用 setChanged() 和 notifyObservers(),Observable 類會自動完成其餘工作。
它還包含一個觀察者列表,並暴露了維護該列表的方法,addObserver() 和 deleteObserver()。
要測試結果,只需將觀察者添加到此列表中並設置新聞即可。
ONewsAgency observable = new ONewsAgency();
ONewsChannel observer = new ONewsChannel();
observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");觀察者接口並非完美,自Java 9版本開始已棄用。 它的一個缺點是Observable不是一個接口,而是一個類,因此子類不能作為可觀察對象使用。
此外,開發者可以覆蓋Observable中的一些同步方法,從而破壞其線程安全性。
4. 使用 PropertyChangeListener 實現
在實現中,一個可觀察對象必須保持對 PropertyChangeSupport 實例的引用。這有助於在類中的屬性發生更改時向觀察者發送通知。
讓我們定義可觀察對象:
public class PCLNewsAgency {
private String news;
private PropertyChangeSupport support;
public PCLNewsAgency() {
support = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener pcl) {
support.addPropertyChangeListener(pcl);
}
public void removePropertyChangeListener(PropertyChangeListener pcl) {
support.removePropertyChangeListener(pcl);
}
public void setNews(String value) {
support.firePropertyChange("news", this.news, value);
this.news = value;
}
}使用這項支持,我們可以添加和刪除觀察者,並在可觀測對象的狀態發生變化時通知他們:
support.firePropertyChange("news", this.news, value);在這裏,第一個參數是觀察到的屬性的名稱。第二個和第三個參數分別是它的舊值和新值。
觀察者應實現 PropertyChangeListener:
public class PCLNewsChannel implements PropertyChangeListener {
private String news;
public void propertyChange(PropertyChangeEvent evt) {
this.setNews((String) evt.getNewValue());
}
// standard getter and setter
}由於 PropertyChangeSupport 類負責處理我們的連接,因此我們可以從事件中恢復新的屬性值。
讓我們測試實現,以確保它也能正常工作:
PCLNewsAgency observable = new PCLNewsAgency();
PCLNewsChannel observer = new PCLNewsChannel();
observable.addPropertyChangeListener(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");5. 失效監聽器問題 (Lapsed Listener Problem)
所有上述實現都需要顯式註銷監聽器以避免失效監聽器問題。遺漏註銷操作可能會導致內存泄漏。
解決此問題的最佳方法是使用弱引用。當監聽器超出作用域時,它將自動從監聽器列表中移除。
6. 結論
在本文中,我們研究了兩種在 Java 中實現 觀察者模式 的方法。我們瞭解到,PropertyChangeListener 方式更具優勢。